feat(aigateway): first-class completion text + provider-agnostic structured output#1515
Conversation
…ctured output Closes #1513, closes #1514. Adds two SDK-level entry points users currently solve themselves: 1. getAIGatewayCompletionText / getAIGatewayCompletionTextResult + AIGatewayClient.completeText() — normalize the assistant text across OpenAI Chat, OpenAI Responses, Anthropic Messages, and Google candidates shapes. The structured result distinguishes 'no textual content' (e.g. tool_calls / length) from an empty-string reply. 2. response_schema on AIGatewayChatCompletionParams + AIGatewayClient.completeStructured() — provider-agnostic structured output. Accepts JSON Schema, Zod, or any StandardSchema with a .toJSONSchema() shortcut. Translated at request time by applyAIGatewayResponseSchema(params) based on model id prefix: openai/*, gpt-*, o[0-9]* -> response_format: { type: 'json_schema' } anthropic/*, claude-* -> forced tool_choice 'submit_response' tool google/*, gemini-* -> generationConfig.responseSchema everything else -> schema-injected system message fallback getAIGatewayCompletionStructured extracts the parsed payload — tool_use input for anthropic, JSON parse (with code-fence stripping) elsewhere. response_format is also now typed as a pass-through field so OpenAI-only users can discover it from TypeScript without reaching for catchall. Verification: - bun test packages/core packages/aigateway -> 578 pass - bun run --filter @agentuity/{core,aigateway,cli,pi} typecheck - bun run --filter @agentuity/{core,aigateway} build - bunx biome lint on touched files
|
The latest Agentuity deployment details.
|
|
Caution Review failedFailed to post review comments 📝 WalkthroughWalkthroughThis PR extends the AIGateway with provider-agnostic structured-output support and enhanced text extraction. A new ChangesAIGateway Structured Outputs and Text Extraction
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
packages/aigateway/src/index.ts (1)
140-141: ⚡ Quick winAvoid translating
response_schematwice incompleteStructured.
AIGatewayService.complete()already applies schema translation, so callingapplyAIGatewayResponseSchema(params)here is redundant and risks pre-mutatingparamsbefore request execution. Compute the family directly instead.Proposed diff
import { AIGatewayService, applyAIGatewayResponseSchema, getAIGatewayCompletionStructured, getAIGatewayCompletionTextResult, + getAIGatewayProviderFamily, type AIGatewayChatCompletion, type AIGatewayChatCompletionParams, type AIGatewayCompletionTextResult, type AIGatewayModels, type AIGatewayProviderFamily, @@ async completeStructured<T = unknown>( params: AIGatewayChatCompletionParams & { response_schema: AIGatewayResponseSchemaInput } ): Promise<{ data: T | undefined; completion: AIGatewayChatCompletion; family: AIGatewayProviderFamily; }> { - const { family } = applyAIGatewayResponseSchema(params); + const family = getAIGatewayProviderFamily(params.model); const completion = await this.#service.complete(params); const data = getAIGatewayCompletionStructured(completion, family) as T | undefined; return { data, completion, family }; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/aigateway/src/index.ts` around lines 140 - 141, The code currently calls applyAIGatewayResponseSchema(params) before invoking AIGatewayService.complete, which duplicates schema translation and may mutate params; remove that call in completeStructured and instead derive the response family directly (e.g., compute the family from params.response_schema or equivalent field) and then call this.#service.complete(params) without pre-applying the schema; update references to applyAIGatewayResponseSchema and ensure completeStructured uses the computed family value only while leaving params unmodified before passing to AIGatewayService.complete.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/core/src/services/aigateway/service.ts`:
- Around line 433-440: The two raw Error throws in the response_schema
conversion branch should be replaced with StructuredError instances to keep
error handling consistent: where the code currently throws new
Error('response_schema: could not convert...') and new Error('response_schema:
expected...'), throw new StructuredError(...) instead, preserving the original
messages and include an appropriate error type/code (e.g., 'InvalidSchema' or
'ResponseSchemaError') and any relevant metadata (the offending value or
validation details); modify the branches handling conversion and the
isPlainJsonSchema check to import and use StructuredError from '`@agentuity/core`'
so callers and telemetry receive typed errors.
- Around line 857-860: The current response builder uses text.length > 0 to set
hasText, which collapses explicit empty-string replies; instead use the
sawTextField boolean (or an equivalent per-part presence flag) to set hasText so
an explicit '' is still reported as true. Update the object construction in the
function that returns { text, hasText: sawTextField && text.length > 0,
...(finishReason ? { finishReason } : {}) } to use hasText: sawTextField (or
sawTextPart where applicable), and apply the same change to the other return
sites noted around the blocks that produce { text, hasText, ...(finishReason...
} (the similar blocks at the other locations referenced) so presence, not
concatenated length, drives hasText.
- Around line 486-489: The current prompt construction in service.ts builds
`preamble` such that when `description` is present it omits the strict "Output
JSON only, no prose, no code fences" instruction, making parsing brittle; update
the `preamble` logic (the code that sets `preamble` and the surrounding return
that uses `name` and `schema`) so that the strict "Output JSON only, no prose,
no code fences" phrase is always included regardless of whether `description`
exists (e.g., append it after the user `description`), ensuring the returned
string that includes `Schema name: ${name}` and `JSON.stringify(schema...)`
always enforces JSON-only output for downstream `parseJsonLoose()` consumers.
- Around line 145-146: The union branch using z.custom for response_schema
currently has no predicate so arbitrary values pass parse; update both
occurrences (the union branch in AIGatewayChatCompletionParamsSchema and the
similar spot at the second occurrence) to use a predicate-backed custom
validator that enforces a non-null object containing the '~standard' key (i.e.,
implement the same check as isStandardSchema) so
AIGatewayChatCompletionParamsSchema.parse(...) will reject invalid
response_schema values early; ensure this matches the runtime expectation used
by toPlainJsonSchema() and use the unique symbols response_schema,
isStandardSchema, toPlainJsonSchema, and AIGatewayChatCompletionParamsSchema to
locate and change the code.
---
Nitpick comments:
In `@packages/aigateway/src/index.ts`:
- Around line 140-141: The code currently calls
applyAIGatewayResponseSchema(params) before invoking AIGatewayService.complete,
which duplicates schema translation and may mutate params; remove that call in
completeStructured and instead derive the response family directly (e.g.,
compute the family from params.response_schema or equivalent field) and then
call this.#service.complete(params) without pre-applying the schema; update
references to applyAIGatewayResponseSchema and ensure completeStructured uses
the computed family value only while leaving params unmodified before passing to
AIGatewayService.complete.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 176c498a-6a39-49ac-b50d-b2d188774c74
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (7)
packages/aigateway/package.jsonpackages/aigateway/src/index.tspackages/aigateway/test/index.test.tspackages/aigateway/tsconfig.test.jsonpackages/core/src/services/aigateway/index.tspackages/core/src/services/aigateway/service.tspackages/core/test/aigateway.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Agentuity Deployment
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: Format code with Biome using tabs (width 3), single quotes, semicolons, lineWidth 100, and trailing commas es5
UseStructuredErrorfrom@agentuity/corefor error handling
Files:
packages/core/src/services/aigateway/index.tspackages/aigateway/test/index.test.tspackages/aigateway/src/index.tspackages/core/test/aigateway.test.tspackages/core/src/services/aigateway/service.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use TypeScript in strict mode with ESNext and bundler moduleResolution
Files:
packages/core/src/services/aigateway/index.tspackages/aigateway/test/index.test.tspackages/aigateway/src/index.tspackages/core/test/aigateway.test.tspackages/core/src/services/aigateway/service.ts
packages/core/src/**/*.ts
📄 CodeRabbit inference engine (packages/core/AGENTS.md)
packages/core/src/**/*.ts: Build TypeScript code usingbun run buildwhich compiles with tsc
Run TypeScript type checking withbun run typecheck
Ensure runtime compatibility with both Browser and Node/Bun environments - no runtime-specific code
Use ESNext as build target with TypeScript declaration files
Use TypeScript-first development - all code must be TypeScript
Prefer interfaces for public APIs
Use generics for reusable type utilities
Ensure no framework coupling - code must work in any JavaScript environment
Many exports aretypeorinterfaceonly - usetypekeyword for type-only exports
Ensure all exports are pure with no side effects or global mutations
All relative imports in TypeScript files MUST include the.tsextension for proper ESM module resolution
Files:
packages/core/src/services/aigateway/index.tspackages/core/src/services/aigateway/service.ts
packages/core/src/services/**/*.ts
📄 CodeRabbit inference engine (packages/core/AGENTS.md)
packages/core/src/services/**/*.ts: Follow StandardSchemaV1 spec for validation interfaces
Storage services must take a FetchAdapter for HTTP abstraction
Files:
packages/core/src/services/aigateway/index.tspackages/core/src/services/aigateway/service.ts
packages/*/test/**/*.test.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
packages/*/test/**/*.test.{ts,tsx,js,jsx}: Place tests intest/folder parallel tosrc/(never insidesrc/or under__tests__/)
Import from../src/in test files
Use@agentuity/test-utilsfor shared mocks in tests
Files:
packages/aigateway/test/index.test.tspackages/core/test/aigateway.test.ts
packages/*/src/index.ts
📄 CodeRabbit inference engine (AGENTS.md)
Use named exports from package
index.tsfiles
Files:
packages/aigateway/src/index.ts
packages/core/**/*.test.ts
📄 CodeRabbit inference engine (packages/core/AGENTS.md)
Use Bun's built-in test runner with
bun testwhen adding tests
Files:
packages/core/test/aigateway.test.ts
🧠 Learnings (3)
📚 Learning: 2026-03-27T23:18:58.450Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 1292
File: packages/keyvalue/package.json:3-3
Timestamp: 2026-03-27T23:18:58.450Z
Learning: In the agentuity/sdk monorepo, subpackage `package.json` files under `packages/` (e.g., `packages/keyvalue`) are allowed to depend on other workspace packages (such as `agentuity/server`) and are not limited to only `agentuity/core` and `zod`. Also, if a subpackage uses `bunx tsc --build --force` as its build script, treat it as a valid/intentional build command and do not flag it as a dependency/build-script violation.
Applied to files:
packages/aigateway/package.json
📚 Learning: 2025-12-21T00:31:41.858Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 274
File: packages/cli/src/cmd/build/vite/server-bundler.ts:12-41
Timestamp: 2025-12-21T00:31:41.858Z
Learning: In Bun runtime, BuildMessage and ResolveMessage are global types and are not exported from the bun module. Do not import { BuildMessage } from 'bun' or similar; these types are available globally and should be used without import. This applies to all TypeScript files that target the Bun runtime within the repository.
Applied to files:
packages/core/src/services/aigateway/index.tspackages/aigateway/test/index.test.tspackages/aigateway/src/index.tspackages/core/test/aigateway.test.tspackages/core/src/services/aigateway/service.ts
📚 Learning: 2026-02-21T02:05:57.982Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 1010
File: packages/drizzle/test/proxy.test.ts:594-603
Timestamp: 2026-02-21T02:05:57.982Z
Learning: Do not rely on StructuredError from agentuity/core in test files or simple error handling paths. In tests and straightforward error handling, use plain Error objects to represent failures, reserving StructuredError for more complex error scenarios in application logic.
Applied to files:
packages/aigateway/test/index.test.tspackages/core/test/aigateway.test.ts
🪛 OpenGrep (1.22.0)
packages/core/src/services/aigateway/service.ts
[ERROR] 646-646: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.
(coderabbit.command-injection.exec-js)
🔇 Additional comments (11)
packages/aigateway/package.json (1)
33-33: LGTM!packages/aigateway/tsconfig.test.json (1)
1-11: LGTM!packages/aigateway/src/index.ts (1)
1-139: LGTM!Also applies to: 142-156
packages/aigateway/test/index.test.ts (1)
1-159: LGTM!packages/core/test/aigateway.test.ts (6)
1-15: LGTM!
660-696: LGTM!
698-819: LGTM!
821-832: LGTM!
834-986: LGTM!
988-1036: LGTM!packages/core/src/services/aigateway/index.ts (1)
11-28: LGTM!
Conflicts in the AI Gateway helpers from #1508. Resolved by: - packages/core/src/services/aigateway/service.ts: keeping the structured extractors and offering #1508's getAIGatewayCompletionText(): string as a thin wrapper around getAIGatewayCompletionTextResult().text. Re-used isUnknownRecord helper from #1508 inside the new extractors for style consistency with the surrounding code. - packages/aigateway/src/index.ts: keeping #1508's resolveOrgId helper + our completeText / completeStructured methods. - packages/aigateway/test/index.test.ts: union of both test files (orgId resolution + completeText/completeStructured). Also applied CodeRabbit review fixes (PR #1515): 1. z.custom<{ '~standard': unknown }>() now uses a predicate so invalid response_schema values fail at parse time instead of leaking into toPlainJsonSchema(). Added a test covering the rejection paths. 2. Raw Error throws in the schema conversion replaced with a typed AIGatewayResponseSchemaError (via StructuredError), matching the convention used elsewhere in this service. 3. The schema-injected system message in the fallback path now always includes 'Output JSON only, no prose, no code fences', even when a description is supplied. 4. hasText now reflects whether a text-bearing field was present, not whether the concatenated text is non-empty. Explicit '' replies now correctly report hasText: true and distinguish from null content that triggered tool_calls / length / refusal. Test updated and an end-to-end test added at the @agentuity/aigateway level. Verification: - bun test packages/core packages/aigateway -> 585 pass / 0 fail - bun run --filter @agentuity/{core,aigateway,cli,pi} typecheck - bun run --filter @agentuity/{core,aigateway} build - bunx biome lint on touched files -> clean
📦 Canary Packages Publishedversion: PackagesInstallAdd to your {
"dependencies": {
"@agentuity/schedule": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-schedule-3.0.1-4336e99.tgz",
"@agentuity/server": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-server-3.0.1-4336e99.tgz",
"@agentuity/db": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-db-3.0.1-4336e99.tgz",
"create-agentuity": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/create-agentuity-3.0.1-4336e99.tgz",
"@agentuity/sandbox": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-sandbox-3.0.1-4336e99.tgz",
"@agentuity/vector": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-vector-3.0.1-4336e99.tgz",
"@agentuity/stream": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-stream-3.0.1-4336e99.tgz",
"@agentuity/postgres": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-postgres-3.0.1-4336e99.tgz",
"@agentuity/coder": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-coder-3.0.1-4336e99.tgz",
"@agentuity/queue": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-queue-3.0.1-4336e99.tgz",
"@agentuity/drizzle": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-drizzle-3.0.1-4336e99.tgz",
"@agentuity/claude-code": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-claude-code-3.0.1-4336e99.tgz",
"@agentuity/pi": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-pi-3.0.1-4336e99.tgz",
"@agentuity/vite": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-vite-3.0.1-4336e99.tgz",
"@agentuity/schema": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-schema-3.0.1-4336e99.tgz",
"@agentuity/coder-tui": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-coder-tui-3.0.1-4336e99.tgz",
"@agentuity/local": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-local-3.0.1-4336e99.tgz",
"@agentuity/task": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-task-3.0.1-4336e99.tgz",
"@agentuity/core": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-core-3.0.1-4336e99.tgz",
"@agentuity/opencode": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-opencode-3.0.1-4336e99.tgz",
"@agentuity/migrate": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-migrate-3.0.1-4336e99.tgz",
"@agentuity/email": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-email-3.0.1-4336e99.tgz",
"@agentuity/keyvalue": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-keyvalue-3.0.1-4336e99.tgz",
"@agentuity/adapter": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-adapter-3.0.1-4336e99.tgz",
"@agentuity/aigateway": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-aigateway-3.0.1-4336e99.tgz",
"@agentuity/cli": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-cli-3.0.1-4336e99.tgz",
"@agentuity/telemetry": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-telemetry-3.0.1-4336e99.tgz",
"@agentuity/webhook": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-webhook-3.0.1-4336e99.tgz",
"@agentuity/analytics": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-analytics-3.0.1-4336e99.tgz",
"@agentuity/hono": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-hono-3.0.1-4336e99.tgz",
"@agentuity/storage": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-storage-3.0.1-4336e99.tgz",
"@agentuity/runtime": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-runtime-3.0.1-4336e99.tgz"
}
}Or install directly: bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-schedule-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-server-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-db-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/create-agentuity-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-sandbox-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-vector-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-stream-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-postgres-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-coder-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-queue-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-drizzle-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-claude-code-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-pi-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-vite-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-schema-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-coder-tui-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-local-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-task-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-core-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-opencode-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-migrate-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-email-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-keyvalue-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-adapter-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-aigateway-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-cli-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-telemetry-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-webhook-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-analytics-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-hono-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-storage-3.0.1-4336e99.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/3.0.1-4336e99/agentuity-runtime-3.0.1-4336e99.tgz |
Closes #1513, closes #1514.
Summary
Two SDK-level entry points that every gateway user currently hand-rolls in the first hour.
#1513 — get the assistant text out of a completion
getAIGatewayCompletionText(completion)→string. Walks the four response shapes the gateway exposes (OpenAI Chat, OpenAI Responses, Anthropic Messages, Google candidates) and concatenates content-parts. Re-exported from@agentuity/aigateway.getAIGatewayCompletionTextResult(completion)→{ text, hasText, finishReason?, toolCalls? }for callers who need to distinguish "the model returned no textual content" (tool_calls / length / refusal) from "the model returned''".AIGatewayClient.completeText(params)ergonomic wrapper that runs the completion and returns the structured result alongside the rawcompletion(so cost / usage / metadata aren't lost).#1514 — provider-agnostic structured output
New typed field on
AIGatewayChatCompletionParams:```ts
const result = await gateway.completeStructured({
model: 'anthropic/claude-haiku-4-5-20251001',
messages: [/* … */],
response_schema: { name: 'finding', schema: FindingZodSchema },
});
// result.data: parsed JSON (Anthropic tool_use.input or JSON-parsed text)
// result.completion: raw completion (usage, cost, metadata)
// result.family: which translation was applied
```
response_schemaaccepts JSON Schema, Zod, any StandardSchema with.toJSONSchema(), or the wrapped form{ name?, description?, strict?, schema }. Translated at request time byapplyAIGatewayResponseSchema(params)based on model id prefix:openai/,gpt-*,o[0-9]*response_format: { type: 'json_schema', json_schema: { schema, strict } }anthropic/,claude-*tool_choicegoogle/,gemini-*generationConfig.responseSchema+responseMimeType: 'application/json'`additionalProperties: false` is recursively added to object schemas when
strict !== false(OpenAI / Google require it).response_formatis also now typed as a pass-through field so OpenAI-only users get it from TypeScript without reaching for catchall.getAIGatewayCompletionStructured(completion, family)extracts the payload — `tool_use.input` for Anthropic, JSON parse (with ```json fence stripping) elsewhere.Non-obvious decisions
Verification
New tests: 17 around `response_schema` translation, provider family detection, structured payload extraction, and 4 in the `@agentuity/aigateway` package exercising the client methods end-to-end via mocked fetch (including a Zod-schema → OpenAI `json_schema` round-trip and an Anthropic tool_use round-trip).
`packages/aigateway` previously had no `test/` directory; this PR adds `packages/aigateway/test/index.test.ts` + `tsconfig.test.json` matching the convention used by `packages/analytics`.
Summary by CodeRabbit
New Features
Tests