Skip to content

feat: add Snowflake Cortex as an AI provider#349

Merged
anandgupta42 merged 9 commits intomainfrom
feat/snowflake-cortext
Mar 22, 2026
Merged

feat: add Snowflake Cortex as an AI provider#349
anandgupta42 merged 9 commits intomainfrom
feat/snowflake-cortext

Conversation

@mdesmet
Copy link
Contributor

@mdesmet mdesmet commented Mar 21, 2026

Adds snowflake-cortex as a built-in provider using Programmatic Access Token (PAT) auth. Users authenticate by entering <account>::<token> once; billing flows through Snowflake credits. Includes Claude, Llama, Mistral, and DeepSeek models with Cortex-specific request transforms (max_completion_tokens, tool stripping for unsupported models, synthetic SSE stop to break the AI SDK's continuation-check loop when Snowflake rejects trailing assistant messages).

Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com

Summary

What changed and why?

Test Plan

How was this tested?

Checklist

  • Tests added/updated
  • Documentation updated (if needed)
  • CHANGELOG updated (if user-facing)

Summary by CodeRabbit

  • New Features
    • Added Snowflake Cortex as a new AI provider with Snowflake PAT auth, automatic request adaptations (parameter renames, model-specific tool handling), and synthetic short-circuit streaming responses to prevent continuation loops.
  • Tests
    • Added comprehensive unit and end-to-end tests covering PAT parsing, request transformations, synthetic-stop behavior, provider loading conditions, and model capability mappings.

Adds `snowflake-cortex` as a built-in provider using Programmatic Access
Token (PAT) auth. Users authenticate by entering `<account>::<token>` once;
billing flows through Snowflake credits. Includes Claude, Llama, Mistral,
and DeepSeek models with Cortex-specific request transforms (max_completion_tokens,
tool stripping for unsupported models, synthetic SSE stop to break the AI
SDK's continuation-check loop when Snowflake rejects trailing assistant messages).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review.

@coderabbitai
Copy link

coderabbitai bot commented Mar 21, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a Snowflake Cortex auth plugin, PAT parser, request-body transformer (with synthetic SSE stop), a provider loader and model registrations for Snowflake Cortex, plus extensive unit and E2E tests covering parsing, transforms, synthetic responses, and provider registration.

Changes

Cohort / File(s) Summary
Snowflake Plugin Core
packages/opencode/src/altimate/plugin/snowflake.ts
New module: VALID_ACCOUNT_RE, parseSnowflakePAT, transformSnowflakeBody (rename max_tokensmax_completion_tokens, strip tools/tool_choice/tool_calls for specified models, filter role: "tool", may return synthetic SSE Response), and SnowflakeCortexAuthPlugin (injects Snowflake headers, overrides fetch, registers Snowflake PAT auth method).
Plugin Registration
packages/opencode/src/plugin/index.ts
Import and register SnowflakeCortexAuthPlugin in internal plugin list so it initializes with other auth plugins.
Provider Loader & Models
packages/opencode/src/provider/provider.ts
Add CUSTOM_LOADERS["snowflake-cortex"] that autoloads when OAuth auth exists, derives/validates account, sets baseURL; add makeSnowflakeModel helper and register Snowflake Cortex provider/models with capability overrides and zeroed costs.
Provider Schema
packages/opencode/src/provider/schema.ts
Add ProviderID.snowflakeCortex static mapped to the literal "snowflake-cortex".
Unit & Integration Tests
packages/opencode/test/provider/snowflake.test.ts, packages/opencode/test/altimate/cortex-snowflake-e2e.test.ts, packages/opencode/test/altimate/drivers-snowflake-e2e.test.ts
Add extensive Bun tests: PAT parsing edge cases, transformSnowflakeBody behavior (token rename, tool/tool_choice/tool_calls handling, synthetic SSE stop and edge cases), provider registration/loading with auth/env permutations, plus optional live E2E tests using env PAT/account. Minor comment tweak in drivers test.

Sequence Diagram

sequenceDiagram
    participant Client as Client
    participant Plugin as SnowflakePlugin
    participant Auth as AuthStore
    participant Transformer as BodyTransformer
    participant API as SnowflakeAPI

    Client->>Plugin: Send request (JSON body, model)
    Plugin->>Auth: Read stored OAuth/PAT (account, token)
    Plugin->>Transformer: Pass body text + model info
    Transformer->>Transformer: Rewrite body (max_tokens→max_completion_tokens)\nStrip tools/tool_choice/tool_calls for specific models\nRemove messages role:"tool"\nDetect synthetic SSE condition
    alt Synthetic SSE produced
        Transformer-->>Plugin: syntheticStop Response (text/event-stream)
        Plugin-->>Client: Return SSE stream ending with [DONE]
    else Forward transformed request
        Plugin->>API: Fetch with headers\nAuthorization: Bearer <token>\nX-Snowflake-Authorization-Token-Type: PROGRAMMATIC_ACCESS_TOKEN
        API-->>Plugin: API response
        Plugin-->>Client: Return API response
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • suryaiyer95

Poem

🐰 I parsed the PAT beneath the moonlit log,
I nudged tokens, swept tools from the bog,
I stitched an SSE hush to end the quest,
Snowflake hums softly — the stream can rest.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is incomplete. While it includes a summary explaining the feature, the 'What changed and why?', 'How was this tested?', and checklist sections are not filled out; the template sections remain empty or unchecked. Complete the missing template sections: expand 'What changed and why?' with details, fill 'How was this tested?' with testing approach, and check the applicable checklist items (tests, documentation, CHANGELOG).
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding Snowflake Cortex as a built-in AI provider. It is concise, clear, and directly reflects the primary objective of the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/snowflake-cortext

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
packages/opencode/test/provider/snowflake.test.ts (2)

208-226: Consider adding claude-3-5-sonnet to the toolcall verification.

The test only checks 2 of the 3 Claude models defined in the provider. Adding claude-3-5-sonnet would provide complete coverage:

 expect(models["claude-sonnet-4-6"].capabilities.toolcall).toBe(true)
 expect(models["claude-haiku-4-5"].capabilities.toolcall).toBe(true)
+expect(models["claude-3-5-sonnet"].capabilities.toolcall).toBe(true)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/provider/snowflake.test.ts` around lines 208 - 226,
The test "claude models have toolcall: true" is missing an assertion for the
third Claude model; update the test inside the Instance.provide fn where you
fetch providers = await Provider.list() and models =
providers["snowflake-cortex"].models to include an assertion that
models["claude-3-5-sonnet"].capabilities.toolcall is true, matching the existing
expect lines for "claude-sonnet-4-6" and "claude-haiku-4-5".

162-166: Consider using the config option for cleaner test setup.

The coding guidelines recommend using the config option with tmpdir() for writing configuration files. While the current init approach works, using config is more concise:

-await using tmp = await tmpdir({
-  init: async (dir) => {
-    await Bun.write(path.join(dir, "opencode.json"), JSON.stringify({ $schema: "https://altimate.ai/config.json" }))
-  },
-})
+await using tmp = await tmpdir({
+  config: { $schema: "https://altimate.ai/config.json" },
+})

This pattern repeats across multiple tests in the file. As per coding guidelines: "Use the config option to write an opencode.json configuration file when tests need project configuration."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/provider/snowflake.test.ts` around lines 162 - 166,
Replace the tmpdir init callback that writes opencode.json with the tmpdir
`config` option: when you call tmpdir(...) (the variable `tmp` and the helper
`tmpdir` in these tests), pass a `config` object that will create opencode.json
containing { $schema: "https://altimate.ai/config.json" } instead of using the
`init` async function that calls Bun.write and path.join; update all similar
blocks in this file to follow the same pattern for a cleaner,
guideline-compliant test setup.
packages/opencode/src/altimate/plugin/snowflake.ts (2)

20-21: JSON.parse can throw on invalid input.

transformSnowflakeBody calls JSON.parse(bodyText) without a try/catch. While the caller in the fetch wrapper (line 106-122) has a catch block, this function is also exported and tested directly. Consider whether it should handle parse errors internally or document that callers must handle exceptions.

💡 Option: Add defensive parsing
 export function transformSnowflakeBody(bodyText: string): { body: string; syntheticStop?: Response } {
-  const parsed = JSON.parse(bodyText)
+  let parsed: Record<string, unknown>
+  try {
+    parsed = JSON.parse(bodyText)
+  } catch {
+    return { body: bodyText }
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/src/altimate/plugin/snowflake.ts` around lines 20 - 21,
transformSnowflakeBody currently calls JSON.parse(bodyText) unguarded; wrap that
parse in a try/catch inside transformSnowflakeBody, and on failure return a
structured failure via the function's return type (e.g., set syntheticStop to a
Response-like error with a 400 status and a clear message including the parse
error) instead of letting the exception bubble; update callers/tests that expect
an exception to use the new syntheticStop path or keep throwing behavior
documented if you choose the opposite approach.

104-123: Body type handling could silently skip transforms for certain types.

When body is not a string or Uint8Array (e.g., Blob, FormData, ReadableStream), the code sets text = "" and skips transformation. This is likely acceptable since AI SDK typically uses string bodies, but worth documenting.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/src/altimate/plugin/snowflake.ts` around lines 104 - 123,
The current body handling silently skips non-string/Uint8Array types (e.g.,
Blob, FormData, ReadableStream) which prevents transformSnowflakeBody from
running; update the logic around init?.body (variable body) so you explicitly
handle common other types before falling back: if body is a Blob call
body.text(), if FormData convert entries to a string (e.g., URLSearchParams or
join key/value pairs), and if it's a ReadableStream read + decode into text (or
use a safe async helper); make the surrounding function async if needed, then
pass the decoded text into transformSnowflakeBody and preserve the existing
syntheticStop handling and reassignment of body.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/opencode/src/altimate/plugin/snowflake.ts`:
- Around line 20-21: transformSnowflakeBody currently calls JSON.parse(bodyText)
unguarded; wrap that parse in a try/catch inside transformSnowflakeBody, and on
failure return a structured failure via the function's return type (e.g., set
syntheticStop to a Response-like error with a 400 status and a clear message
including the parse error) instead of letting the exception bubble; update
callers/tests that expect an exception to use the new syntheticStop path or keep
throwing behavior documented if you choose the opposite approach.
- Around line 104-123: The current body handling silently skips
non-string/Uint8Array types (e.g., Blob, FormData, ReadableStream) which
prevents transformSnowflakeBody from running; update the logic around init?.body
(variable body) so you explicitly handle common other types before falling back:
if body is a Blob call body.text(), if FormData convert entries to a string
(e.g., URLSearchParams or join key/value pairs), and if it's a ReadableStream
read + decode into text (or use a safe async helper); make the surrounding
function async if needed, then pass the decoded text into transformSnowflakeBody
and preserve the existing syntheticStop handling and reassignment of body.

In `@packages/opencode/test/provider/snowflake.test.ts`:
- Around line 208-226: The test "claude models have toolcall: true" is missing
an assertion for the third Claude model; update the test inside the
Instance.provide fn where you fetch providers = await Provider.list() and models
= providers["snowflake-cortex"].models to include an assertion that
models["claude-3-5-sonnet"].capabilities.toolcall is true, matching the existing
expect lines for "claude-sonnet-4-6" and "claude-haiku-4-5".
- Around line 162-166: Replace the tmpdir init callback that writes
opencode.json with the tmpdir `config` option: when you call tmpdir(...) (the
variable `tmp` and the helper `tmpdir` in these tests), pass a `config` object
that will create opencode.json containing { $schema:
"https://altimate.ai/config.json" } instead of using the `init` async function
that calls Bun.write and path.join; update all similar blocks in this file to
follow the same pattern for a cleaner, guideline-compliant test setup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 837e2689-7ac9-486c-add2-915f0c4fc688

📥 Commits

Reviewing files that changed from the base of the PR and between 623d1ac and d03ba0d.

📒 Files selected for processing (5)
  • packages/opencode/src/altimate/plugin/snowflake.ts
  • packages/opencode/src/plugin/index.ts
  • packages/opencode/src/provider/provider.ts
  • packages/opencode/src/provider/schema.ts
  • packages/opencode/test/provider/snowflake.test.ts

…and edge case fixes

- Add `altimate_change` markers to all upstream-shared files (`provider.ts`,
  `schema.ts`, `plugin/index.ts`) to prevent overwrites on upstream merges
- Validate account ID against `^[a-zA-Z0-9._-]+$` to prevent URL injection
- Remove `(auth as any).accountId` casts — use proper type narrowing
- Fix `env` array: `SNOWFLAKE_PAT` → `SNOWFLAKE_ACCOUNT` (matches actual usage)
- Fix `claude-3-5-sonnet` output limit: `8096` → `8192`
- Strip orphaned `tool_calls` and `tool` role messages for no-toolcall models
- Use explicit `Array.isArray(tool_calls)` check for synthetic stop condition
- Remove zero-usage block from synthetic SSE to avoid broken token accounting
- Handle `ArrayBuffer` body type in fetch wrapper
- Reduce PAT expiry from 365 → 90 days (matches Snowflake default TTL)
- Add 14 new test cases covering URL injection, orphaned tool_calls, empty
  tool_calls array, SSE format validation, missing messages, and env/output limits

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/opencode/test/provider/snowflake.test.ts (1)

256-260: Use tmpdir({ config: ... }) for these fixtures.

These blocks all hand-write the same opencode.json, which is exactly what the config option already encapsulates. Switching the repeated setup to tmpdir({ config: { $schema: "https://altimate.ai/config.json" } }) will remove a lot of noise from this suite. As per coding guidelines, "Use the config option to write an opencode.json configuration file when tests need project configuration".

Also applies to: 281-285, 303-307, 323-327, 344-348, 365-369, 383-387

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/provider/snowflake.test.ts` around lines 256 - 260,
Replace the manual writing of opencode.json inside tmpdir init blocks with the
tmpdir config option: instead of using tmpdir({ init: async (dir) =>
Bun.write(path.join(dir, "opencode.json"), JSON.stringify({ $schema:
"https://altimate.ai/config.json" })) }), call tmpdir({ config: { $schema:
"https://altimate.ai/config.json" } }); update every similar tmpdir usage in
this test file (the other tmpdir fixtures that hand-write opencode.json) to use
the config option to reduce duplication.
packages/opencode/src/altimate/plugin/snowflake.ts (1)

4-4: Keep the no-toolcall model matrix in one place.

NO_TOOLCALL_MODELS duplicates the toolcall: false capability overrides in packages/opencode/src/provider/provider.ts Lines 945-947. If one list changes without the other, model metadata and request rewriting will disagree. Consider exporting a shared Snowflake capability map and deriving this set from it.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/src/altimate/plugin/snowflake.ts` at line 4, The
NO_TOOLCALL_MODELS constant in snowflake.ts duplicates the toolcall:false
capability overrides in provider.ts; instead, export a single Snowflake
capability map (e.g., SNOWFLAKE_CAPABILITY_OVERRIDES or
SNOWFLAKE_MODEL_CAPABILITIES) from the provider module that contains per-model
capabilities, update provider.ts to use that exported map for its overrides, and
in packages/opencode/src/altimate/plugin/snowflake.ts import that map and derive
NO_TOOLCALL_MODELS from entries whose capability has toolcall:false; remove the
hardcoded array from snowflake.ts so both request rewriting and metadata read
from the same source.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/opencode/src/provider/provider.ts`:
- Around line 674-685: The provider should not treat SNOWFLAKE_ACCOUNT env as a
credential fallback: remove the Env.get("SNOWFLAKE_ACCOUNT") fallback in the
"snowflake-cortex" provider construction so account is derived only from
Auth.get("snowflake-cortex") (auth.type === "oauth" && auth.accountId) and
return { autoload: false } when absent; update provider.env/metadata and any
loader logic so state() and getSDK() no longer accept or copy SNOWFLAKE_ACCOUNT
into apiKey (ensure getSDK() pulls the PAT from the proper auth object), and
rely on the existing account validation in the snowflake plugin rather than
interpolating env values into baseURL.

In `@packages/opencode/test/provider/snowflake.test.ts`:
- Around line 276-299: The test mutates the global auth.json via
Auth.get/Auth.remove/Auth.set which makes it non-hermetic and racy; instead,
make the test use an isolated auth store by configuring or stubbing Auth to
point at the temp fixture created by tmpdir (or inject a temporary in-memory
auth implementation) during the Instance.provide run so the global file is not
touched. Update the test to create a temp auth path or mock Auth methods
(Auth.get, Auth.remove, Auth.set) for the duration of the test (restore original
behavior afterwards) or pass the temp auth location into Instance.provide/init
so Provider.list("snowflake-cortex") is evaluated against the isolated store.
Ensure cleanup restores the real Auth behavior only after the isolated instance
completes.

---

Nitpick comments:
In `@packages/opencode/src/altimate/plugin/snowflake.ts`:
- Line 4: The NO_TOOLCALL_MODELS constant in snowflake.ts duplicates the
toolcall:false capability overrides in provider.ts; instead, export a single
Snowflake capability map (e.g., SNOWFLAKE_CAPABILITY_OVERRIDES or
SNOWFLAKE_MODEL_CAPABILITIES) from the provider module that contains per-model
capabilities, update provider.ts to use that exported map for its overrides, and
in packages/opencode/src/altimate/plugin/snowflake.ts import that map and derive
NO_TOOLCALL_MODELS from entries whose capability has toolcall:false; remove the
hardcoded array from snowflake.ts so both request rewriting and metadata read
from the same source.

In `@packages/opencode/test/provider/snowflake.test.ts`:
- Around line 256-260: Replace the manual writing of opencode.json inside tmpdir
init blocks with the tmpdir config option: instead of using tmpdir({ init: async
(dir) => Bun.write(path.join(dir, "opencode.json"), JSON.stringify({ $schema:
"https://altimate.ai/config.json" })) }), call tmpdir({ config: { $schema:
"https://altimate.ai/config.json" } }); update every similar tmpdir usage in
this test file (the other tmpdir fixtures that hand-write opencode.json) to use
the config option to reduce duplication.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 85839961-72e4-4abd-a74f-28cefb14ebe7

📥 Commits

Reviewing files that changed from the base of the PR and between d03ba0d and e498403.

📒 Files selected for processing (5)
  • packages/opencode/src/altimate/plugin/snowflake.ts
  • packages/opencode/src/plugin/index.ts
  • packages/opencode/src/provider/provider.ts
  • packages/opencode/src/provider/schema.ts
  • packages/opencode/test/provider/snowflake.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/opencode/src/plugin/index.ts
  • packages/opencode/src/provider/schema.ts

Comment on lines +674 to +685
"snowflake-cortex": async () => {
const auth = await Auth.get("snowflake-cortex")
const account = iife(() => {
if (auth?.type === "oauth" && auth.accountId) return auth.accountId
return Env.get("SNOWFLAKE_ACCOUNT")
})
if (!account) return { autoload: false }
return {
autoload: true,
options: {
baseURL: `https://${account}.snowflakecomputing.com/api/v2/cortex/v1`,
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't expose SNOWFLAKE_ACCOUNT as a provider credential.

provider.env is consumed as auth input in state() (Lines 1053-1058), and getSDK() later copies a single env value into apiKey on Line 1248. With the current loader + metadata, SNOWFLAKE_ACCOUNT=myorg makes the provider look configured even when no PAT exists, so requests go out as Authorization: Bearer myorg and never get the Snowflake PAT header from packages/opencode/src/altimate/plugin/snowflake.ts Lines 91-139. The env fallback also bypasses the account validation in packages/opencode/src/altimate/plugin/snowflake.ts Lines 10-17, so malformed values are interpolated straight into baseURL.

Suggested direction
     "snowflake-cortex": async () => {
       const auth = await Auth.get("snowflake-cortex")
-      const account = iife(() => {
-        if (auth?.type === "oauth" && auth.accountId) return auth.accountId
-        return Env.get("SNOWFLAKE_ACCOUNT")
-      })
-      if (!account) return { autoload: false }
+      if (auth?.type !== "oauth") return { autoload: false }
+      const account = auth.accountId ?? Env.get("SNOWFLAKE_ACCOUNT")
+      if (!account || !/^[a-zA-Z0-9._-]+$/.test(account)) return { autoload: false }
       return {
         autoload: true,
         options: {
           baseURL: `https://${account}.snowflakecomputing.com/api/v2/cortex/v1`,
@@
     database["snowflake-cortex"] = {
       id: ProviderID.snowflakeCortex,
       source: "custom",
       name: "Snowflake Cortex",
-      env: ["SNOWFLAKE_ACCOUNT"],
+      env: [],

Also applies to: 935-939

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/src/provider/provider.ts` around lines 674 - 685, The
provider should not treat SNOWFLAKE_ACCOUNT env as a credential fallback: remove
the Env.get("SNOWFLAKE_ACCOUNT") fallback in the "snowflake-cortex" provider
construction so account is derived only from Auth.get("snowflake-cortex")
(auth.type === "oauth" && auth.accountId) and return { autoload: false } when
absent; update provider.env/metadata and any loader logic so state() and
getSDK() no longer accept or copy SNOWFLAKE_ACCOUNT into apiKey (ensure getSDK()
pulls the PAT from the proper auth object), and rely on the existing account
validation in the snowflake plugin rather than interpolating env values into
baseURL.

… credential

Addresses CodeRabbit review comments:
- Require `auth.type === "oauth"` before autoloading — env-only `SNOWFLAKE_ACCOUNT`
  no longer makes the provider look configured without a PAT
- Set `env: []` so `state()` env-key scan doesn't treat account name as an API key
- Validate account from env fallback against `^[a-zA-Z0-9._-]+$`
- Add test: env-only without oauth does NOT load the provider
- All provider tests now set up/teardown oauth auth properly via save/restore
- Update env array assertion: `toContain("SNOWFLAKE_ACCOUNT")` → `toEqual([])`

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
packages/opencode/test/provider/snowflake.test.ts (1)

255-273: ⚠️ Potential issue | 🟠 Major

These tests still mutate the shared auth store.

Saving and restoring the previous snowflake-cortex entry reduces fallout, but Auth.get/Auth.set/Auth.remove are still hitting the real store and can race with any other auth-related test. Please point Auth at the temp fixture or stub it inside Instance.provide instead of editing global credentials.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/provider/snowflake.test.ts` around lines 255 - 273,
The tests mutate the real Auth store via Auth.get/Auth.set/Auth.remove; instead
inject or stub a test-specific Auth instance inside Instance.provide (or pass a
mock Auth into the provider) so the global credential store is not
touched—replace calls to Auth.get/Auth.set/Auth.remove in the setup/restore
helpers by using a temporary in-memory auth fixture or sinon/jest stubs scoped
to the test and ensure they are restored after each test; locate the
setup/restore helpers and the call site that calls Instance.provide and switch
to dependency-injecting the mocked Auth implementation instead of editing the
global store.
🧹 Nitpick comments (1)
packages/opencode/test/provider/snowflake.test.ts (1)

278-282: Use tmpdir({ config }) for this setup instead of writing opencode.json in init.

These blocks repeat the same fixture bootstrapping that the helper already supports, and switching to config would drop the manual path.join/Bun.write boilerplate. As per coding guidelines, "Use the config option to write an opencode.json configuration file when tests need project configuration".

Also applies to: 302-306, 326-330, 349-353, 371-375, 394-398, 417-421, 437-441

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/provider/snowflake.test.ts` around lines 278 - 282,
Replace manual opencode.json creation in tmpdir init blocks with the tmpdir
config option: instead of using tmpdir({ init: async (dir) =>
Bun.write(path.join(dir, "opencode.json"), JSON.stringify(...)) }), call
tmpdir({ config: { $schema: "https://altimate.ai/config.json" } }) so the helper
writes the opencode.json for you; update each occurrence in
packages/opencode/test/provider/snowflake.test.ts (the tmpdir calls that
currently create opencode.json in init) to use tmpdir(..., config) and remove
the path.join/Bun.write boilerplate.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/opencode/src/provider/provider.ts`:
- Around line 675-678: The current branch returning { autoload: false } still
allows the Snowflake auth plugin (Auth.get("snowflake-cortex")) to merge into
providers earlier, leaving a broken provider when accountId is missing or
invalid; update the logic so that before any plugin-auth merge or provider
registration you validate auth.accountId (and Env.get("SNOWFLAKE_ACCOUNT")) with
the regex used here, and if invalid either (a) prevent the plugin from merging
the Snowflake provider into the providers list (gate the merge on the same
validation) or (b) explicitly remove/drop the provider entry after
makeSnowflakeModel() would have run so providers does not retain an entry with
an empty api.url; ensure the same check is applied wherever provider
registration for Snowflake occurs (the code paths around
Auth.get("snowflake-cortex"), makeSnowflakeModel(), and CUSTOM_LOADERS).

In `@packages/opencode/test/provider/snowflake.test.ts`:
- Around line 309-310: The test setup mutates shared Env (calling
Env.remove/Env.set) but only restores auth in the finally blocks, leaking
SNOWFLAKE_ACCOUNT across tests; update the test init/cleanup logic (the init
async functions and their corresponding finally/teardown blocks around the
provider loading in packages/opencode/test/provider/snowflake.test.ts) to
capture the original Env.get("SNOWFLAKE_ACCOUNT") before mutating and restore it
in the finally/after step (analogous to how auth is restored), and do the same
for the other affected blocks referenced around lines 333-334 so each test
restores SNOWFLAKE_ACCOUNT to avoid test order dependency.

---

Duplicate comments:
In `@packages/opencode/test/provider/snowflake.test.ts`:
- Around line 255-273: The tests mutate the real Auth store via
Auth.get/Auth.set/Auth.remove; instead inject or stub a test-specific Auth
instance inside Instance.provide (or pass a mock Auth into the provider) so the
global credential store is not touched—replace calls to
Auth.get/Auth.set/Auth.remove in the setup/restore helpers by using a temporary
in-memory auth fixture or sinon/jest stubs scoped to the test and ensure they
are restored after each test; locate the setup/restore helpers and the call site
that calls Instance.provide and switch to dependency-injecting the mocked Auth
implementation instead of editing the global store.

---

Nitpick comments:
In `@packages/opencode/test/provider/snowflake.test.ts`:
- Around line 278-282: Replace manual opencode.json creation in tmpdir init
blocks with the tmpdir config option: instead of using tmpdir({ init: async
(dir) => Bun.write(path.join(dir, "opencode.json"), JSON.stringify(...)) }),
call tmpdir({ config: { $schema: "https://altimate.ai/config.json" } }) so the
helper writes the opencode.json for you; update each occurrence in
packages/opencode/test/provider/snowflake.test.ts (the tmpdir calls that
currently create opencode.json in init) to use tmpdir(..., config) and remove
the path.join/Bun.write boilerplate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f2a65e70-f9af-48c2-a279-fd009e73b984

📥 Commits

Reviewing files that changed from the base of the PR and between e498403 and 7167ef0.

📒 Files selected for processing (2)
  • packages/opencode/src/provider/provider.ts
  • packages/opencode/test/provider/snowflake.test.ts

Comment on lines +675 to +678
const auth = await Auth.get("snowflake-cortex")
if (auth?.type !== "oauth") return { autoload: false }
const account = auth.accountId ?? Env.get("SNOWFLAKE_ACCOUNT")
if (!account || !/^[a-zA-Z0-9._-]+$/.test(account)) return { autoload: false }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

autoload: false here does not actually disable an invalid Snowflake provider.

The Snowflake auth plugin added in this PR can already merge snowflake-cortex into providers before CUSTOM_LOADERS runs, so this branch still leaves the provider registered when accountId is missing or fails the regex. Because makeSnowflakeModel() leaves api.url empty, that surviving provider has no Cortex base URL to fall back to. Please gate the plugin-auth merge on a valid account, or explicitly drop the provider when this validation fails.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/src/provider/provider.ts` around lines 675 - 678, The
current branch returning { autoload: false } still allows the Snowflake auth
plugin (Auth.get("snowflake-cortex")) to merge into providers earlier, leaving a
broken provider when accountId is missing or invalid; update the logic so that
before any plugin-auth merge or provider registration you validate
auth.accountId (and Env.get("SNOWFLAKE_ACCOUNT")) with the regex used here, and
if invalid either (a) prevent the plugin from merging the Snowflake provider
into the providers list (gate the merge on the same validation) or (b)
explicitly remove/drop the provider entry after makeSnowflakeModel() would have
run so providers does not retain an entry with an empty api.url; ensure the same
check is applied wherever provider registration for Snowflake occurs (the code
paths around Auth.get("snowflake-cortex"), makeSnowflakeModel(), and
CUSTOM_LOADERS).

Comment on lines +309 to +310
init: async () => {
Env.remove("SNOWFLAKE_ACCOUNT")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Restore SNOWFLAKE_ACCOUNT after each test.

Env.remove and Env.set mutate shared Env state here, but the finally blocks only restore auth. That leaks Snowflake config into later tests and makes provider loading order-dependent.

Also applies to: 333-334

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/provider/snowflake.test.ts` around lines 309 - 310,
The test setup mutates shared Env (calling Env.remove/Env.set) but only restores
auth in the finally blocks, leaking SNOWFLAKE_ACCOUNT across tests; update the
test init/cleanup logic (the init async functions and their corresponding
finally/teardown blocks around the provider loading in
packages/opencode/test/provider/snowflake.test.ts) to capture the original
Env.get("SNOWFLAKE_ACCOUNT") before mutating and restore it in the finally/after
step (analogous to how auth is restored), and do the same for the other affected
blocks referenced around lines 333-334 so each test restores SNOWFLAKE_ACCOUNT
to avoid test order dependency.

Fixes from 6-model consensus review (Claude, GPT 5.2, Gemini 3.1,
Kimi K2.5, MiniMax M2.5, GLM-5):

- Gate synthetic SSE stop on `stream !== false` to avoid returning SSE
  format for non-streaming requests (Major, flagged by GPT 5.2 Codex)
- Delete `content-length` header after body mutation to prevent
  length mismatch (Minor, flagged by GPT 5.2/Kimi/Gemini consensus)
- Export `VALID_ACCOUNT_RE` from `snowflake.ts` and import in
  `provider.ts` to eliminate duplicated regex (Minor, flagged by GLM-5)
- Add `claude-3-5-sonnet` to toolcall capability test (Kimi K2.5)
- Add 3 new tests: `stream: false` skips synthetic stop, `stream: true`
  triggers it, absent `stream` field defaults to streaming behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/opencode/src/provider/provider.ts (1)

929-929: Consider using accurate release dates for models.

The hardcoded release_date: "2024-01-01" is a placeholder that doesn't reflect actual model release dates. Users may rely on release dates for model selection or compliance purposes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/src/provider/provider.ts` at line 929, The hardcoded
release_date value ("2024-01-01") in the provider metadata should be replaced
with the accurate model release date or removed/derived dynamically; locate the
release_date entry in the provider metadata object (the `release_date` key in
packages/opencode/src/provider/provider.ts) and either update it to the correct
ISO date for the model, fetch/provide the canonical release date from the model
metadata source, or omit the field if the true date is unknown, ensuring
downstream consumers get accurate or explicitly absent release information.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/opencode/src/provider/provider.ts`:
- Line 929: The hardcoded release_date value ("2024-01-01") in the provider
metadata should be replaced with the accurate model release date or
removed/derived dynamically; locate the release_date entry in the provider
metadata object (the `release_date` key in
packages/opencode/src/provider/provider.ts) and either update it to the correct
ISO date for the model, fetch/provide the canonical release date from the model
metadata source, or omit the field if the true date is unknown, ensuring
downstream consumers get accurate or explicitly absent release information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1763051b-06da-443b-83d1-8710dc1794ce

📥 Commits

Reviewing files that changed from the base of the PR and between 7167ef0 and bba5356.

📒 Files selected for processing (3)
  • packages/opencode/src/altimate/plugin/snowflake.ts
  • packages/opencode/src/provider/provider.ts
  • packages/opencode/test/provider/snowflake.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/opencode/src/altimate/plugin/snowflake.ts
  • packages/opencode/test/provider/snowflake.test.ts

- Add `cortex-snowflake-e2e.test.ts` with 16 E2E tests for the Snowflake
  Cortex AI provider: PAT auth, streaming/non-streaming completions, model
  availability, request transforms, assistant-last rejection, PAT parsing
- Tests skip via `describe.skipIf` when `SNOWFLAKE_CORTEX_PAT` is not set
- Remove hardcoded credentials from `drivers-snowflake-e2e.test.ts` docstring
  — replaced with placeholder values

Run with:
  export SNOWFLAKE_CORTEX_ACCOUNT="<account>"
  export SNOWFLAKE_CORTEX_PAT="<pat>"
  bun test test/altimate/cortex-snowflake-e2e.test.ts --timeout 120000

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@anandgupta42 anandgupta42 force-pushed the feat/snowflake-cortext branch from 43041a4 to 512915a Compare March 21, 2026 21:33
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/opencode/test/altimate/cortex-snowflake-e2e.test.ts`:
- Around line 35-73: The test helper cortexChat splits account/PAT and sets the
headers/body itself, bypassing the provider's production parsing/transform;
change cortexChat (and any callers) to exercise the real provider contract by
accepting a single combined credential (e.g. "<account>::<pat>") and sending it
in the Authorization header (Authorization: `Bearer ${combinedCred}`) rather
than setting account and PAT separately, and use the provider-facing field name
for token limits (send max_tokens if that matches production) so the provider's
request-transform/path and streaming happy path are exercised; update
cortexBaseURL usage only to build the endpoint path, and keep cortexChat as the
single entry that mimics the production auth shape.
- Around line 300-307: The test "parses real account::pat format" currently
asserts the live PAT value directly; update it to avoid exposing
SNOWFLAKE_CORTEX_PAT by removing expect(result!.token).toBe(pat) and instead
assert only non-sensitive properties—e.g., confirm
parseSnowflakePAT(`${account}::${pat}`) returns non-null, result!.account ===
CORTEX_ACCOUNT, and result!.token is a non-empty string (or matches a safe
pattern/length check). Keep references to parseSnowflakePAT, CORTEX_ACCOUNT, and
CORTEX_PAT so the change is localized.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4d730ae2-c60e-4bae-a238-611f162263d5

📥 Commits

Reviewing files that changed from the base of the PR and between bba5356 and 43041a4.

📒 Files selected for processing (2)
  • packages/opencode/test/altimate/cortex-snowflake-e2e.test.ts
  • packages/opencode/test/altimate/drivers-snowflake-e2e.test.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/opencode/test/altimate/drivers-snowflake-e2e.test.ts

Comment on lines +35 to +73
const CORTEX_ACCOUNT = process.env.SNOWFLAKE_CORTEX_ACCOUNT
const CORTEX_PAT = process.env.SNOWFLAKE_CORTEX_PAT
const HAS_CORTEX = !!(CORTEX_ACCOUNT && CORTEX_PAT)

function cortexBaseURL(account: string): string {
return `https://${account}.snowflakecomputing.com/api/v2/cortex/v1`
}

/** Make a raw Cortex chat completion request. */
async function cortexChat(opts: {
account: string
pat: string
model: string
messages: Array<{ role: string; content: string }>
stream?: boolean
max_tokens?: number
tools?: unknown[]
}): Promise<Response> {
const body: Record<string, unknown> = {
model: opts.model,
messages: opts.messages,
max_completion_tokens: opts.max_tokens ?? 256,
}
if (opts.stream !== undefined) body.stream = opts.stream
if (opts.tools) {
body.tools = opts.tools
body.tool_choice = "auto"
}

return fetch(`${cortexBaseURL(opts.account)}/chat/completions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${opts.pat}`,
"X-Snowflake-Authorization-Token-Type": "PROGRAMMATIC_ACCESS_TOKEN",
},
body: JSON.stringify(body),
})
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Route at least one smoke path through the real provider contract.

This helper splits account/pat, sets the auth headers itself, and already sends max_completion_tokens, so the live calls never exercise the provider’s actual <account>::<token> parsing or request-transform chain. For an E2E suite of the new provider, at least the happy-path and streaming smoke tests should go through the same entry point/config shape production uses.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/altimate/cortex-snowflake-e2e.test.ts` around lines 35
- 73, The test helper cortexChat splits account/PAT and sets the headers/body
itself, bypassing the provider's production parsing/transform; change cortexChat
(and any callers) to exercise the real provider contract by accepting a single
combined credential (e.g. "<account>::<pat>") and sending it in the
Authorization header (Authorization: `Bearer ${combinedCred}`) rather than
setting account and PAT separately, and use the provider-facing field name for
token limits (send max_tokens if that matches production) so the provider's
request-transform/path and streaming happy path are exercised; update
cortexBaseURL usage only to build the endpoint path, and keep cortexChat as the
single entry that mimics the production auth shape.

Comment on lines +300 to +307
test("parses real account::pat format", () => {
const account = CORTEX_ACCOUNT!
const pat = CORTEX_PAT!
const result = parseSnowflakePAT(`${account}::${pat}`)
expect(result).not.toBeNull()
expect(result!.account).toBe(account)
expect(result!.token).toBe(pat)
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd packages/opencode/test && find . -name "*cortex-snowflake*" -type f

Repository: AltimateAI/altimate-code

Length of output: 107


🏁 Script executed:

head -320 packages/opencode/test/altimate/cortex-snowflake-e2e.test.ts | tail -50

Repository: AltimateAI/altimate-code

Length of output: 1945


🏁 Script executed:

grep -n "CORTEX_PAT\|CORTEX_ACCOUNT" packages/opencode/test/altimate/cortex-snowflake-e2e.test.ts | head -20

Repository: AltimateAI/altimate-code

Length of output: 1079


🏁 Script executed:

cat -n packages/opencode/test/altimate/cortex-snowflake-e2e.test.ts | sed -n '1,20p'

Repository: AltimateAI/altimate-code

Length of output: 842


🏁 Script executed:

cat -n packages/opencode/test/altimate/cortex-snowflake-e2e.test.ts | sed -n '295,315p'

Repository: AltimateAI/altimate-code

Length of output: 934


🏁 Script executed:

grep -B5 -A10 "parses real account::pat format" packages/opencode/test/altimate/cortex-snowflake-e2e.test.ts

Repository: AltimateAI/altimate-code

Length of output: 759


🏁 Script executed:

grep -n "expect.*CORTEX_PAT\|expect.*pat.*toBe\|expect.*token.*toBe" packages/opencode/test/altimate/cortex-snowflake-e2e.test.ts

Repository: AltimateAI/altimate-code

Length of output: 341


Avoid asserting the live PAT value directly.

If this fails, the assertion diff can expose SNOWFLAKE_CORTEX_PAT in CI logs. Keep exact token round-trip checks in unit tests with fake credentials and only assert non-sensitive properties here.

🔐 Safer assertion
       const result = parseSnowflakePAT(`${account}::${pat}`)
       expect(result).not.toBeNull()
       expect(result!.account).toBe(account)
-      expect(result!.token).toBe(pat)
+      expect(result!.token).toBeTruthy()
+      expect(result!.token.length).toBe(pat.length)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/altimate/cortex-snowflake-e2e.test.ts` around lines
300 - 307, The test "parses real account::pat format" currently asserts the live
PAT value directly; update it to avoid exposing SNOWFLAKE_CORTEX_PAT by removing
expect(result!.token).toBe(pat) and instead assert only non-sensitive
properties—e.g., confirm parseSnowflakePAT(`${account}::${pat}`) returns
non-null, result!.account === CORTEX_ACCOUNT, and result!.token is a non-empty
string (or matches a safe pattern/length check). Keep references to
parseSnowflakePAT, CORTEX_ACCOUNT, and CORTEX_PAT so the change is localized.

anandgupta42 and others added 4 commits March 21, 2026 15:04
Verified against Snowflake account ejjkbko-fub20041 with cross-region
inference enabled. Key findings and fixes:

Model list changes (from real API probing):
- Replace `llama3.3-70b` (unavailable) with `snowflake-llama-3.3-70b`
- Add `llama3.1-70b`, `llama3.1-405b`, `llama3.1-8b`, `mistral-7b`
- All 10 models verified against live Cortex endpoint

Tool calling fix:
- Switch from blocklist (`NO_TOOLCALL_MODELS`) to allowlist
  (`TOOLCALL_MODELS`) — only Claude models support tool calls on Cortex,
  all others reject with "tool calling is not supported"

E2E test improvements (24 tests, all pass against live API):
- Test all 10 registered models for availability and response shape
- Tool call support test: Claude accepts, non-Claude rejects
- DeepSeek R1 reasoning format test (`<think>` tags in content)
- Support key-pair JWT auth (no PAT required)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
From official Snowflake documentation (docs.snowflake.com):

New models added (19 → 28 total):
- OpenAI: `openai-gpt-4.1`, `openai-gpt-5`, `openai-gpt-5-mini`,
  `openai-gpt-5-nano`, `openai-gpt-5-chat`, `openai-gpt-oss-120b`
- Claude: `claude-opus-4-6`, `claude-sonnet-4-5`, `claude-opus-4-5`,
  `claude-4-sonnet`, `claude-4-opus`, `claude-3-7-sonnet`
- Meta: `llama4-maverick`, `mistral-large`

Tool calling update:
- Per docs: "Tool calling is supported for OpenAI and Claude models only"
- Updated `TOOLCALL_MODELS` allowlist to include all OpenAI + Claude IDs

Note: OpenAI models were not available on this test account (returned
"unknown model") but are documented in the Cortex REST API reference.
Availability depends on region and account configuration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Probed all 28 documented models against ejjkbko-fub20041 with
cross-region enabled:

Verified working (13):
- Claude: claude-sonnet-4-6, claude-opus-4-6, claude-sonnet-4-5,
  claude-opus-4-5, claude-haiku-4-5, claude-4-sonnet, claude-3-7-sonnet,
  claude-3-5-sonnet
- OpenAI: openai-gpt-4.1, openai-gpt-5, openai-gpt-5-mini,
  openai-gpt-5-nano, openai-gpt-5-chat
- OpenAI tool calling confirmed working (get_weather test)

Removed from registration (kept as comments):
- claude-4-opus: 403 "account not allowed" (gated)
- openai-gpt-oss-120b: 500 internal error (not stable)

Also verified:
- llama4-maverick, mistral-large: working
- GPT-5 preview variants return 200 but empty content (preview)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Documentation:
- Add Snowflake Cortex section to docs/configure/providers.md with
  auth instructions, model table, and cross-region note
- Add Snowflake Cortex to model format reference in models.md
- Add v0.5.6 changelog entry

Test gap fixes (46 → 52 unit tests):
- Content-length deletion after body transform
- Synthetic stop returns valid SSE Response object
- Both max_tokens + max_completion_tokens present (max_tokens wins)
- Unknown model tools stripped (allowlist default)
- tool_choice without tools stripped for non-toolcall models
- max_completion_tokens preserved when max_tokens absent

E2E model validation (37 pass against live API):
- All 26 registered models probed: 21 available, 4 gated/broken, 1 preview
- Accept 200/400/403/500 for model availability (accounts vary)
- Handle preview models returning empty content (openai-gpt-5-*)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@anandgupta42 anandgupta42 merged commit c4f5c16 into main Mar 22, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants