Skip to content

Back ChatSnowflake() with the Cortex REST API: thin Anthropic/OpenAI provider subclasses, model-name dispatch, credentials-callable auth #432

Description

@cpsievert

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions