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

    Priority: MediumValid bug or well-defined request with moderate impact or a workaround.ai-triage:doneMarks an issue whose AI triage workflow is complete.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions