Proposal: back ChatSnowflake() with the Cortex REST API, delegating to AnthropicProvider/OpenAICompletionsProvider
Context
claude-sonnet-5 on Snowflake Cortex sends extended thinking blocks that the current SnowflakeProvider (built on snowflake-ml-python's CompleteRequest) never had to handle. Supporting them requires Snowflake-specific parsing/merging logic for thinking + signature fragments that duplicates what AnthropicProvider already does. Every future Anthropic (or OpenAI) feature Snowflake adopts — redacted thinking, interleaved thinking, server tools, new delta types — will require another patch against Snowflake's proprietary, undocumented streaming format, which the provider's own comments already flag as unstable. (Short-term fix for the immediate crash: branch fix/snowflake-anthropic-thinking-delta.)
The opportunity
Snowflake's Cortex REST API exposes two industry-standard endpoints:
|
Chat Completions API |
Messages API |
| Compatibility |
OpenAI Chat Completions |
Anthropic Messages API |
| Endpoint |
/api/v2/cortex/v1/chat/completions |
/api/v2/cortex/v1/messages |
| Models |
All (Claude, OpenAI, Llama, Mistral, ...) |
Claude only (incl. claude-sonnet-5) |
| SDK |
openai |
anthropic |
Both are explicitly documented to work with the stock SDKs by overriding base_url and setting a Bearer token. The Messages API supports adaptive/extended thinking with signatures that round-trip in multi-turn conversations, proper SSE streaming, Anthropic-format tool calling, prompt caching, structured output, and anthropic-beta headers (interleaved thinking, effort, etc.).
Proposal
Combine two patterns already in chatlas:
Subclassing mechanics from BedrockMessagesProvider (chatlas/_provider_bedrock.py): thin subclasses that customize client construction and inherit everything else.
@no_file_management # no Files API on the gateway
class SnowflakeAnthropicProvider(AnthropicProvider):
"""Reaches Snowflake Cortex's Anthropic Messages API."""
# ~60 lines: anthropic.Anthropic with
# base_url="https://<account>.snowflakecomputing.com/api/v2/cortex"
class SnowflakeOpenAIProvider(OpenAICompletionsProvider):
"""Reaches Snowflake Cortex's OpenAI Chat Completions API."""
# base_url="https://<account>.snowflakecomputing.com/api/v2/cortex/v1"
Dispatch and auth ergonomics from ChatPosit (chatlas/_provider_posit.py):
- Dispatch by model name, not an
api= param. ChatPosit selects its Anthropic vs OpenAI provider via model.startswith("claude"). Snowflake's endpoint coverage is cleanly partitioned by model family (Claude → Messages, everything else → Chat Completions), so unlike Bedrock — where api= exists because the APIs overlap in model coverage — no explicit api= argument is needed. (An optional override could still force the legacy path during a transition period.)
- A
credentials: Callable[[], str] parameter + an httpx auth flow that swaps the SDK's default x-api-key header for Authorization: Bearer <token> — exactly what Snowflake requires, per its own docs. Because the callable is invoked per request, expiring tokens (OAuth, key-pair JWT) are naturally supported. One callable serves both REST flavors.
Benefits:
- All Anthropic/OpenAI thinking, tool-use, and streaming logic is inherited, not duplicated; future Snowflake-adopted features arrive for free.
- Thinking signatures round-trip correctly in multi-turn tool loops (the
CompleteRequest path currently drops ContentThinking when re-serializing history).
- End state mirrors
ChatPosit: two thin provider subclasses plus auth, and the snowflake-ml-python dependency — along with its janky stream-merging code — can be deprecated rather than patched.
Open questions / costs
- Auth is the main work. The REST API needs a Bearer token: PAT (simplest for users), OAuth, or key-pair JWT (
X-Snowflake-Authorization-Token-Type: KEYPAIR_JWT; the header is optional since Snowflake sniffs token type). To preserve the current connections.toml / private_key_file ergonomics, chatlas should ship a default credentials helper that mints/refreshes a JWT from the existing connection params. Session tokens should be discouraged: their expiration surfaces as HTTP 200 with error code 390112, which would need a response-body-inspecting error hook.
- Observability differs: REST API calls don't write to
AI_OBSERVABILITY_EVENTS; usage moves to CORTEX_REST_API_USAGE_HISTORY. Worth documenting.
- Transition plan: keep the legacy provider as the default initially, gate the new path behind opt-in (or new-model dispatch), and deprecate once the auth story is proven.
Related
- Short-term thinking-delta fix on the legacy path: branch
fix/snowflake-anthropic-thinking-delta
Proposal: back
ChatSnowflake()with the Cortex REST API, delegating toAnthropicProvider/OpenAICompletionsProviderContext
claude-sonnet-5on Snowflake Cortex sends extended thinking blocks that the currentSnowflakeProvider(built onsnowflake-ml-python'sCompleteRequest) never had to handle. Supporting them requires Snowflake-specific parsing/merging logic for thinking + signature fragments that duplicates whatAnthropicProvideralready does. Every future Anthropic (or OpenAI) feature Snowflake adopts — redacted thinking, interleaved thinking, server tools, new delta types — will require another patch against Snowflake's proprietary, undocumented streaming format, which the provider's own comments already flag as unstable. (Short-term fix for the immediate crash: branchfix/snowflake-anthropic-thinking-delta.)The opportunity
Snowflake's Cortex REST API exposes two industry-standard endpoints:
/api/v2/cortex/v1/chat/completions/api/v2/cortex/v1/messagesclaude-sonnet-5)openaianthropicBoth are explicitly documented to work with the stock SDKs by overriding
base_urland setting aBearertoken. The Messages API supports adaptive/extended thinking with signatures that round-trip in multi-turn conversations, proper SSE streaming, Anthropic-format tool calling, prompt caching, structured output, andanthropic-betaheaders (interleaved thinking, effort, etc.).Proposal
Combine two patterns already in chatlas:
Subclassing mechanics from
BedrockMessagesProvider(chatlas/_provider_bedrock.py): thin subclasses that customize client construction and inherit everything else.Dispatch and auth ergonomics from
ChatPosit(chatlas/_provider_posit.py):api=param.ChatPositselects its Anthropic vs OpenAI provider viamodel.startswith("claude"). Snowflake's endpoint coverage is cleanly partitioned by model family (Claude → Messages, everything else → Chat Completions), so unlike Bedrock — whereapi=exists because the APIs overlap in model coverage — no explicitapi=argument is needed. (An optional override could still force the legacy path during a transition period.)credentials: Callable[[], str]parameter + an httpx auth flow that swaps the SDK's defaultx-api-keyheader forAuthorization: Bearer <token>— exactly what Snowflake requires, per its own docs. Because the callable is invoked per request, expiring tokens (OAuth, key-pair JWT) are naturally supported. One callable serves both REST flavors.Benefits:
CompleteRequestpath currently dropsContentThinkingwhen re-serializing history).ChatPosit: two thin provider subclasses plus auth, and thesnowflake-ml-pythondependency — along with its janky stream-merging code — can be deprecated rather than patched.Open questions / costs
X-Snowflake-Authorization-Token-Type: KEYPAIR_JWT; the header is optional since Snowflake sniffs token type). To preserve the currentconnections.toml/private_key_fileergonomics, chatlas should ship a default credentials helper that mints/refreshes a JWT from the existing connection params. Session tokens should be discouraged: their expiration surfaces as HTTP 200 with error code390112, which would need a response-body-inspecting error hook.AI_OBSERVABILITY_EVENTS; usage moves toCORTEX_REST_API_USAGE_HISTORY. Worth documenting.Related
fix/snowflake-anthropic-thinking-delta