posit-dev / posit-dev/chatlas

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

Open
#432 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

ai-triage:done Priority: Medium
Dominant language
Python
Stars
176
Forks
28
Avg merge
18h 42m
Merged PRs (30d)
16

Description

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

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read chatlas/_provider_bedrock.py and chatlas/_provider_posit.py first to compare subclass construction, model dispatch, and credentials handling. Then inspect the existing SnowflakeProvider and its tests or entry points, if present. Done means both Cortex REST API paths, callable Bearer authentication, dispatch, transition behavior, and documented observability differences are resolved and tested.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
ai, backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.