@genui-a3/providers 0.0.0 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,186 @@
1
+ # @genui-a3/providers
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@genui-a3/providers)](https://www.npmjs.com/package/@genui-a3/providers)
4
+ [![license](https://img.shields.io/npm/l/@genui-a3/providers)](https://github.com/generalui/a3/blob/main/LICENSE)
5
+
6
+ **LLM provider implementations for the [A3 agentic framework](https://www.npmjs.com/package/@genui-a3/core).**
7
+
8
+ Ships with **AWS Bedrock**, **Anthropic** and **OpenAI** providers out of the box.
9
+ Both support blocking and streaming modes, model fallback, and structured output via Zod schemas.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @genui-a3/providers @genui-a3/core
15
+ ```
16
+
17
+ `@genui-a3/core` is a **peer dependency** — it must be installed alongside this package.
18
+
19
+ ## Quick Start
20
+
21
+ ### AWS Bedrock
22
+
23
+ ```typescript
24
+ import { createBedrockProvider } from '@genui-a3/providers/bedrock'
25
+
26
+ const provider = createBedrockProvider({
27
+ models: ['us.anthropic.claude-sonnet-4-5-20250929-v1:0'],
28
+ region: 'us-east-1', // optional, defaults to AWS SDK default
29
+ })
30
+ ```
31
+
32
+ ### OpenAI
33
+
34
+ ```typescript
35
+ import { createOpenAIProvider } from '@genui-a3/providers/openai'
36
+
37
+ const provider = createOpenAIProvider({
38
+ models: ['gpt-4o', 'gpt-4o-mini'],
39
+ apiKey: process.env.OPENAI_API_KEY, // optional, defaults to OPENAI_API_KEY env var
40
+ })
41
+ ```
42
+
43
+ ### Use with A3
44
+
45
+ ```typescript
46
+ import { ChatSession, MemorySessionStore } from '@genui-a3/core'
47
+
48
+ const session = new ChatSession({
49
+ sessionId: 'user-123',
50
+ store: new MemorySessionStore(),
51
+ initialAgentId: 'greeting',
52
+ initialState: {},
53
+ provider, // any provider from above
54
+ })
55
+
56
+ // Blocking
57
+ const response = await session.send({ message: 'Hello!' })
58
+
59
+ // Streaming
60
+ for await (const event of session.send({ message: 'Hello!', stream: true })) {
61
+ console.log(event)
62
+ }
63
+ ```
64
+
65
+ ## Providers
66
+
67
+ ### Bedrock — `createBedrockProvider(config)`
68
+
69
+ Communicates with AWS Bedrock via the [Converse API](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_Converse.html).
70
+
71
+ | Option | Type | Required | Description |
72
+ |---|---|---|---|
73
+ | `models` | `string[]` | Yes | Model IDs in preference order (first = primary, rest = fallbacks) |
74
+ | `region` | `string` | No | AWS region. Defaults to AWS SDK default |
75
+
76
+ **Behaviour:**
77
+
78
+ - Uses **tool-based JSON extraction** (`structuredResponse` tool) for reliable structured output
79
+ - **Streaming** yields text deltas in real-time, then emits a validated tool-call result at the end
80
+ - **Merges sequential same-role messages** to satisfy Bedrock's alternating-role requirement
81
+ - **Prepends an initial user message** (`"Hi"`) so the conversation always starts with a user turn
82
+
83
+ **Prerequisites:** AWS credentials configured via environment variables, IAM role, or AWS profile — the same setup the AWS SDK expects.
84
+
85
+ ---
86
+
87
+ ### OpenAI — `createOpenAIProvider(config)`
88
+
89
+ Communicates with the OpenAI Chat Completions API using [structured output](https://platform.openai.com/docs/guides/structured-outputs) (`response_format: json_schema`).
90
+
91
+ | Option | Type | Required | Description |
92
+ |---|---|---|---|
93
+ | `models` | `string[]` | Yes | Model IDs in preference order (first = primary, rest = fallbacks) |
94
+ | `apiKey` | `string` | No | API key. Defaults to `OPENAI_API_KEY` env var |
95
+ | `baseURL` | `string` | No | Custom base URL for Azure OpenAI or compatible endpoints |
96
+ | `organization` | `string` | No | OpenAI organization ID |
97
+
98
+ **Behaviour:**
99
+
100
+ - Uses **structured output** (`response_format` with `json_schema`) — no tool calls required
101
+ - **Enforces strict schemas** automatically (`additionalProperties: false`, all properties `required`)
102
+ - **Streaming** extracts `chatbotMessage` text progressively from the JSON response via a character-level state machine, yielding text deltas in real-time
103
+ - Detects **truncated responses** (`finish_reason: length`) and surfaces them as errors
104
+
105
+ **Prerequisites:** An OpenAI API key, either passed directly or set as `OPENAI_API_KEY`.
106
+
107
+ ## Model Fallback
108
+
109
+ Both providers support automatic model fallback. List models in order of preference:
110
+
111
+ ```typescript
112
+ const provider = createBedrockProvider({
113
+ models: [
114
+ 'us.anthropic.claude-sonnet-4-5-20250929-v1:0', // primary
115
+ 'us.anthropic.claude-haiku-4-5-20251001-v1:0', // fallback
116
+ ],
117
+ })
118
+ ```
119
+
120
+ If the primary model fails, the provider automatically retries with the next model in the list. If all models fail, the last error is thrown.
121
+
122
+ ## Per-Agent Provider Override
123
+
124
+ Each agent can override the session-level provider:
125
+
126
+ ```typescript
127
+ import { createOpenAIProvider } from '@genui-a3/providers/openai'
128
+ import { createBedrockProvider } from '@genui-a3/providers/bedrock'
129
+
130
+ // Session uses Bedrock by default
131
+ const session = new ChatSession({
132
+ provider: createBedrockProvider({ models: ['us.anthropic.claude-sonnet-4-5-20250929-v1:0'] }),
133
+ // ...
134
+ })
135
+
136
+ // This agent uses OpenAI instead
137
+ const premiumAgent = {
138
+ id: 'premium',
139
+ provider: createOpenAIProvider({ models: ['gpt-4o'] }),
140
+ // ...
141
+ }
142
+ ```
143
+
144
+ ## Provider Interface
145
+
146
+ Both providers implement the `Provider` interface from `@genui-a3/core`:
147
+
148
+ | Member | Description |
149
+ |---|---|
150
+ | `sendRequest(request)` | Blocking request → `Promise<ProviderResponse>` |
151
+ | `sendRequestStream(request)` | Streaming request → `AsyncGenerator<StreamEvent>` |
152
+ | `name` | Human-readable name (`'bedrock'` or `'openai'`) |
153
+
154
+ To create a custom provider, implement this interface and pass it to `ChatSession` or an individual agent.
155
+
156
+ ## Exports
157
+
158
+ This package uses [subpath exports](https://nodejs.org/api/packages.html#subpath-exports). Import from the specific provider entry point:
159
+
160
+ ```typescript
161
+ // ✅ Correct
162
+ import { createBedrockProvider } from '@genui-a3/providers/bedrock'
163
+ import { createOpenAIProvider } from '@genui-a3/providers/openai'
164
+
165
+ // ❌ No bare import
166
+ import { ... } from '@genui-a3/providers'
167
+ ```
168
+
169
+ | Entry point | Export | Description |
170
+ |---|---|---|
171
+ | `@genui-a3/providers/bedrock` | `createBedrockProvider` | Factory function returning a Bedrock `Provider` |
172
+ | `@genui-a3/providers/bedrock` | `BedrockProviderConfig` | TypeScript config interface |
173
+ | `@genui-a3/providers/openai` | `createOpenAIProvider` | Factory function returning an OpenAI `Provider` |
174
+ | `@genui-a3/providers/openai` | `OpenAIProviderConfig` | TypeScript config interface |
175
+
176
+ ## Requirements
177
+
178
+ - Node.js 20.19.0+
179
+ - TypeScript 5.9+
180
+ - `@genui-a3/core` ≥ 0.1.5 (peer dependency)
181
+ - **Bedrock**: AWS credentials configured in the environment
182
+ - **OpenAI**: `OPENAI_API_KEY` environment variable or `apiKey` config option
183
+
184
+ ## License
185
+
186
+ ISC
@@ -0,0 +1,162 @@
1
+ 'use strict';
2
+
3
+ var anthropic = require('@ai-sdk/anthropic');
4
+ var ai = require('ai');
5
+ var client = require('@ag-ui/client');
6
+
7
+ // anthropic/index.ts
8
+ async function* processAnthropicStream(streamResult, reader, first, agentId, schema) {
9
+ let prevMessageLength = 0;
10
+ try {
11
+ if (!first.done) {
12
+ const partial = first.value;
13
+ const delta = extractDelta(partial, prevMessageLength);
14
+ if (delta) {
15
+ prevMessageLength += delta.length;
16
+ yield {
17
+ type: client.EventType.TEXT_MESSAGE_CONTENT,
18
+ messageId: "",
19
+ delta,
20
+ agentId
21
+ };
22
+ }
23
+ }
24
+ let next = await reader.next();
25
+ while (!next.done) {
26
+ const partial = next.value;
27
+ const delta = extractDelta(partial, prevMessageLength);
28
+ if (delta) {
29
+ prevMessageLength += delta.length;
30
+ yield {
31
+ type: client.EventType.TEXT_MESSAGE_CONTENT,
32
+ messageId: "",
33
+ delta,
34
+ agentId
35
+ };
36
+ }
37
+ next = await reader.next();
38
+ }
39
+ const finalObject = await streamResult.output;
40
+ if (finalObject === null) {
41
+ yield {
42
+ type: client.EventType.RUN_ERROR,
43
+ message: "Anthropic stream completed with null output",
44
+ agentId
45
+ };
46
+ return;
47
+ }
48
+ const validated = schema.parse(finalObject);
49
+ yield {
50
+ type: client.EventType.TOOL_CALL_RESULT,
51
+ toolCallId: "",
52
+ messageId: "",
53
+ content: JSON.stringify(validated),
54
+ agentId
55
+ };
56
+ } catch (err) {
57
+ yield {
58
+ type: client.EventType.RUN_ERROR,
59
+ message: `Anthropic stream error: ${err.message}`,
60
+ agentId
61
+ };
62
+ }
63
+ }
64
+ function extractDelta(partial, prevLength) {
65
+ const chatbotMessage = partial.chatbotMessage;
66
+ if (typeof chatbotMessage !== "string" || chatbotMessage.length <= prevLength) {
67
+ return null;
68
+ }
69
+ return chatbotMessage.slice(prevLength);
70
+ }
71
+
72
+ // utils/executeWithFallback.ts
73
+ async function executeWithFallback(models, action) {
74
+ for (let i = 0; i < models.length; i++) {
75
+ const model = models[i];
76
+ try {
77
+ return await action(model);
78
+ } catch (error) {
79
+ const errorObj = error;
80
+ if (i === models.length - 1) {
81
+ throw errorObj;
82
+ }
83
+ }
84
+ }
85
+ throw new Error("All models failed");
86
+ }
87
+
88
+ // anthropic/index.ts
89
+ function toAIMessages(messages) {
90
+ return messages.map((msg) => ({
91
+ role: msg.role,
92
+ content: msg.content
93
+ }));
94
+ }
95
+ function prepareMessages(messages) {
96
+ if (messages.length === 0) return messages;
97
+ const lastMessage = messages[messages.length - 1];
98
+ if (lastMessage.role === "assistant") {
99
+ return [...messages, { role: "user", content: "Continue" }];
100
+ }
101
+ return messages;
102
+ }
103
+ async function sendWithModel(anthropicProvider, model, system, messages, schema) {
104
+ const preparedMessages = prepareMessages(messages);
105
+ const result = await ai.generateText({
106
+ model: anthropicProvider(model),
107
+ system,
108
+ messages: preparedMessages,
109
+ output: ai.Output.object({ schema })
110
+ });
111
+ return {
112
+ content: JSON.stringify(result.output),
113
+ usage: {
114
+ inputTokens: result.usage.inputTokens ?? 0,
115
+ outputTokens: result.usage.outputTokens ?? 0,
116
+ totalTokens: (result.usage.inputTokens ?? 0) + (result.usage.outputTokens ?? 0)
117
+ }
118
+ };
119
+ }
120
+ async function sendStreamWithModel(anthropicProvider, model, system, messages, schema) {
121
+ const preparedMessages = prepareMessages(messages);
122
+ const result = ai.streamText({
123
+ model: anthropicProvider(model),
124
+ system,
125
+ messages: preparedMessages,
126
+ output: ai.Output.object({ schema })
127
+ });
128
+ const partialStream = result.partialOutputStream;
129
+ const reader = partialStream[Symbol.asyncIterator]();
130
+ const first = await reader.next();
131
+ return { result, reader, first };
132
+ }
133
+ function createAnthropicProvider(config) {
134
+ const anthropicProvider = anthropic.createAnthropic({
135
+ apiKey: config.apiKey,
136
+ baseURL: config.baseURL
137
+ });
138
+ const models = config.models;
139
+ return {
140
+ name: "anthropic",
141
+ async sendRequest(request) {
142
+ const messages = toAIMessages(request.messages);
143
+ return executeWithFallback(
144
+ models,
145
+ (model) => sendWithModel(anthropicProvider, model, request.systemPrompt, messages, request.responseSchema)
146
+ );
147
+ },
148
+ async *sendRequestStream(request) {
149
+ const messages = toAIMessages(request.messages);
150
+ const { result, reader, first } = await executeWithFallback(
151
+ models,
152
+ (model) => sendStreamWithModel(anthropicProvider, model, request.systemPrompt, messages, request.responseSchema)
153
+ );
154
+ yield* processAnthropicStream(result, reader, first, "anthropic", request.responseSchema);
155
+ }
156
+ };
157
+ }
158
+
159
+ exports.createAnthropicProvider = createAnthropicProvider;
160
+ exports.prepareMessages = prepareMessages;
161
+ //# sourceMappingURL=index.cjs.map
162
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../anthropic/streamProcessor.ts","../../utils/executeWithFallback.ts","../../anthropic/index.ts"],"names":["EventType","generateText","Output","streamText","createAnthropic"],"mappings":"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","file":"index.cjs","sourcesContent":["import { EventType } from '@ag-ui/client'\nimport { ZodType } from 'zod'\nimport type { AgentId, StreamEvent, BaseState } from '@genui-a3/core'\nimport type { StreamTextResult, ToolSet } from 'ai'\n\n/**\n * Processes an Anthropic streaming response (via Vercel AI SDK) into AG-UI events.\n *\n * Uses `partialOutputStream` from `streamText` + `Output.object()` to receive\n * progressively-built partial objects. Tracks `chatbotMessage` growth to yield\n * TEXT_MESSAGE_CONTENT deltas. After the stream completes, validates the final\n * object and yields TOOL_CALL_RESULT.\n *\n * @param streamResult - The streamText result containing partialOutputStream and output promise\n * @param reader - Pre-started async iterator for the partial object stream\n * @param first - The first iteration result (already consumed to trigger the API call)\n * @param agentId - Agent identifier for event tagging\n * @param schema - Zod schema for final response validation\n * @returns Async generator of AG-UI stream events\n */\nexport async function* processAnthropicStream<TState extends BaseState = BaseState>(\n streamResult: StreamTextResult<ToolSet, never>,\n reader: AsyncIterator<unknown>,\n first: IteratorResult<unknown>,\n agentId: AgentId,\n schema: ZodType,\n): AsyncGenerator<StreamEvent<TState>> {\n let prevMessageLength = 0\n\n try {\n // Process the first partial (already consumed to trigger the API call)\n if (!first.done) {\n const partial = first.value as Record<string, unknown>\n const delta = extractDelta(partial, prevMessageLength)\n if (delta) {\n prevMessageLength += delta.length\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: '',\n delta,\n agentId,\n } as StreamEvent<TState>\n }\n }\n\n // Process remaining partials\n let next = await reader.next()\n while (!next.done) {\n const partial = next.value as Record<string, unknown>\n const delta = extractDelta(partial, prevMessageLength)\n if (delta) {\n prevMessageLength += delta.length\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: '',\n delta,\n agentId,\n } as StreamEvent<TState>\n }\n // eslint-disable-next-line no-await-in-loop\n next = await reader.next()\n }\n\n // Stream complete — await and validate the final object\n const finalObject = await streamResult.output\n\n if (finalObject === null) {\n yield {\n type: EventType.RUN_ERROR,\n message: 'Anthropic stream completed with null output',\n agentId,\n } as StreamEvent<TState>\n return\n }\n\n const validated = schema.parse(finalObject)\n yield {\n type: EventType.TOOL_CALL_RESULT,\n toolCallId: '',\n messageId: '',\n content: JSON.stringify(validated),\n agentId,\n } as StreamEvent<TState>\n } catch (err) {\n yield {\n type: EventType.RUN_ERROR,\n message: `Anthropic stream error: ${(err as Error).message}`,\n agentId,\n } as StreamEvent<TState>\n }\n}\n\n/**\n * Extracts the new portion of chatbotMessage from a partial object.\n */\nfunction extractDelta(partial: Record<string, unknown>, prevLength: number): string | null {\n const chatbotMessage = partial.chatbotMessage\n if (typeof chatbotMessage !== 'string' || chatbotMessage.length <= prevLength) {\n return null\n }\n return chatbotMessage.slice(prevLength)\n}\n","/**\n * Executes an action with model fallback support.\n * Tries each model in order; if one fails, falls back to the next.\n * Throws the last error if all models fail.\n *\n * @param models - Model identifiers in priority order\n * @param action - Async action to attempt with each model\n * @returns The result from the first successful model\n * @throws The error from the last model if all fail\n *\n * @example\n * ```typescript\n * const result = await executeWithFallback(\n * ['model-primary', 'model-fallback'],\n * (model) => provider.call(model, params),\n * )\n * ```\n */\nexport async function executeWithFallback<T>(models: string[], action: (model: string) => Promise<T>): Promise<T> {\n const errors: Array<{ model: string; error: Error }> = []\n\n for (let i = 0; i < models.length; i++) {\n const model = models[i]\n\n try {\n // eslint-disable-next-line no-await-in-loop\n return await action(model)\n } catch (error) {\n const errorObj = error as Error\n errors.push({ model, error: errorObj })\n\n if (i === models.length - 1) {\n throw errorObj\n }\n }\n }\n\n throw new Error('All models failed')\n}\n","import { createAnthropic } from '@ai-sdk/anthropic'\nimport { generateText, streamText, Output, ModelMessage } from 'ai'\nimport type {\n Provider,\n ProviderRequest,\n ProviderResponse,\n ProviderMessage,\n BaseState,\n StreamEvent,\n} from '@genui-a3/core'\nimport { processAnthropicStream } from './streamProcessor'\nimport { executeWithFallback } from '../utils/executeWithFallback'\n\n/**\n * Configuration for creating an Anthropic provider.\n */\nexport interface AnthropicProviderConfig {\n /** Anthropic API key. Defaults to ANTHROPIC_API_KEY env var. */\n apiKey?: string\n /**\n * Model identifiers in order of preference (first = primary, rest = fallbacks).\n * e.g. ['claude-sonnet-4-5-20250929', 'claude-haiku-4-5-20251001']\n */\n models: string[]\n /** Optional custom base URL for the Anthropic API */\n baseURL?: string\n}\n\nfunction toAIMessages(messages: ProviderMessage[]): ModelMessage[] {\n return messages.map((msg) => ({\n role: msg.role,\n content: msg.content,\n }))\n}\n\nexport function prepareMessages(messages: ModelMessage[]): ModelMessage[] {\n if (messages.length === 0) return messages\n const lastMessage = messages[messages.length - 1]\n if (lastMessage.role === 'assistant') {\n return [...messages, { role: 'user', content: 'Continue' }]\n }\n return messages\n}\n\nasync function sendWithModel(\n anthropicProvider: ReturnType<typeof createAnthropic>,\n model: string,\n system: string,\n messages: ModelMessage[],\n schema: ProviderRequest['responseSchema'],\n): Promise<ProviderResponse> {\n const preparedMessages = prepareMessages(messages)\n const result = await generateText({\n model: anthropicProvider(model),\n system,\n messages: preparedMessages,\n output: Output.object({ schema }),\n })\n\n return {\n content: JSON.stringify(result.output),\n usage: {\n inputTokens: result.usage.inputTokens ?? 0,\n outputTokens: result.usage.outputTokens ?? 0,\n totalTokens: (result.usage.inputTokens ?? 0) + (result.usage.outputTokens ?? 0),\n },\n }\n}\n\nasync function sendStreamWithModel(\n anthropicProvider: ReturnType<typeof createAnthropic>,\n model: string,\n system: string,\n messages: ModelMessage[],\n schema: ProviderRequest['responseSchema'],\n) {\n const preparedMessages = prepareMessages(messages)\n const result = streamText({\n model: anthropicProvider(model),\n system,\n messages: preparedMessages,\n output: Output.object({ schema }),\n })\n\n // Force the API call to start so executeWithFallback can catch connection errors\n const partialStream = result.partialOutputStream\n const reader = partialStream[Symbol.asyncIterator]()\n const first = await reader.next()\n\n return { result, reader, first }\n}\n\n/**\n * Creates an Anthropic provider instance.\n *\n * Uses the Vercel AI SDK (`ai` + `@ai-sdk/anthropic`) for structured output via\n * `generateText` + `Output.object()` (blocking) and `streamText` + `Output.object()`\n * (streaming). The AI SDK handles Zod-to-JSON-schema conversion, partial JSON\n * parsing, and validation internally.\n *\n * @param config - Anthropic provider configuration\n * @returns A Provider implementation using Anthropic\n *\n * @example\n * ```typescript\n * const provider = createAnthropicProvider({\n * models: ['claude-sonnet-4-5-20250929', 'claude-haiku-4-5-20251001'],\n * })\n * ```\n */\nexport function createAnthropicProvider(config: AnthropicProviderConfig): Provider {\n const anthropicProvider = createAnthropic({\n apiKey: config.apiKey,\n baseURL: config.baseURL,\n })\n const models = config.models\n\n return {\n name: 'anthropic',\n\n async sendRequest(request: ProviderRequest): Promise<ProviderResponse> {\n const messages = toAIMessages(request.messages)\n\n return executeWithFallback(models, (model) =>\n sendWithModel(anthropicProvider, model, request.systemPrompt, messages, request.responseSchema),\n )\n },\n\n async *sendRequestStream<TState extends BaseState = BaseState>(\n request: ProviderRequest,\n ): AsyncGenerator<StreamEvent<TState>> {\n const messages = toAIMessages(request.messages)\n\n const { result, reader, first } = await executeWithFallback(models, (model) =>\n sendStreamWithModel(anthropicProvider, model, request.systemPrompt, messages, request.responseSchema),\n )\n\n yield* processAnthropicStream<TState>(result, reader, first, 'anthropic', request.responseSchema)\n },\n }\n}\n"]}
@@ -0,0 +1,39 @@
1
+ import { ModelMessage } from 'ai';
2
+ import { Provider } from '@genui-a3/core';
3
+
4
+ /**
5
+ * Configuration for creating an Anthropic provider.
6
+ */
7
+ interface AnthropicProviderConfig {
8
+ /** Anthropic API key. Defaults to ANTHROPIC_API_KEY env var. */
9
+ apiKey?: string;
10
+ /**
11
+ * Model identifiers in order of preference (first = primary, rest = fallbacks).
12
+ * e.g. ['claude-sonnet-4-5-20250929', 'claude-haiku-4-5-20251001']
13
+ */
14
+ models: string[];
15
+ /** Optional custom base URL for the Anthropic API */
16
+ baseURL?: string;
17
+ }
18
+ declare function prepareMessages(messages: ModelMessage[]): ModelMessage[];
19
+ /**
20
+ * Creates an Anthropic provider instance.
21
+ *
22
+ * Uses the Vercel AI SDK (`ai` + `@ai-sdk/anthropic`) for structured output via
23
+ * `generateText` + `Output.object()` (blocking) and `streamText` + `Output.object()`
24
+ * (streaming). The AI SDK handles Zod-to-JSON-schema conversion, partial JSON
25
+ * parsing, and validation internally.
26
+ *
27
+ * @param config - Anthropic provider configuration
28
+ * @returns A Provider implementation using Anthropic
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const provider = createAnthropicProvider({
33
+ * models: ['claude-sonnet-4-5-20250929', 'claude-haiku-4-5-20251001'],
34
+ * })
35
+ * ```
36
+ */
37
+ declare function createAnthropicProvider(config: AnthropicProviderConfig): Provider;
38
+
39
+ export { type AnthropicProviderConfig, createAnthropicProvider, prepareMessages };
@@ -0,0 +1,39 @@
1
+ import { ModelMessage } from 'ai';
2
+ import { Provider } from '@genui-a3/core';
3
+
4
+ /**
5
+ * Configuration for creating an Anthropic provider.
6
+ */
7
+ interface AnthropicProviderConfig {
8
+ /** Anthropic API key. Defaults to ANTHROPIC_API_KEY env var. */
9
+ apiKey?: string;
10
+ /**
11
+ * Model identifiers in order of preference (first = primary, rest = fallbacks).
12
+ * e.g. ['claude-sonnet-4-5-20250929', 'claude-haiku-4-5-20251001']
13
+ */
14
+ models: string[];
15
+ /** Optional custom base URL for the Anthropic API */
16
+ baseURL?: string;
17
+ }
18
+ declare function prepareMessages(messages: ModelMessage[]): ModelMessage[];
19
+ /**
20
+ * Creates an Anthropic provider instance.
21
+ *
22
+ * Uses the Vercel AI SDK (`ai` + `@ai-sdk/anthropic`) for structured output via
23
+ * `generateText` + `Output.object()` (blocking) and `streamText` + `Output.object()`
24
+ * (streaming). The AI SDK handles Zod-to-JSON-schema conversion, partial JSON
25
+ * parsing, and validation internally.
26
+ *
27
+ * @param config - Anthropic provider configuration
28
+ * @returns A Provider implementation using Anthropic
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const provider = createAnthropicProvider({
33
+ * models: ['claude-sonnet-4-5-20250929', 'claude-haiku-4-5-20251001'],
34
+ * })
35
+ * ```
36
+ */
37
+ declare function createAnthropicProvider(config: AnthropicProviderConfig): Provider;
38
+
39
+ export { type AnthropicProviderConfig, createAnthropicProvider, prepareMessages };
@@ -0,0 +1,159 @@
1
+ import { createAnthropic } from '@ai-sdk/anthropic';
2
+ import { streamText, Output, generateText } from 'ai';
3
+ import { EventType } from '@ag-ui/client';
4
+
5
+ // anthropic/index.ts
6
+ async function* processAnthropicStream(streamResult, reader, first, agentId, schema) {
7
+ let prevMessageLength = 0;
8
+ try {
9
+ if (!first.done) {
10
+ const partial = first.value;
11
+ const delta = extractDelta(partial, prevMessageLength);
12
+ if (delta) {
13
+ prevMessageLength += delta.length;
14
+ yield {
15
+ type: EventType.TEXT_MESSAGE_CONTENT,
16
+ messageId: "",
17
+ delta,
18
+ agentId
19
+ };
20
+ }
21
+ }
22
+ let next = await reader.next();
23
+ while (!next.done) {
24
+ const partial = next.value;
25
+ const delta = extractDelta(partial, prevMessageLength);
26
+ if (delta) {
27
+ prevMessageLength += delta.length;
28
+ yield {
29
+ type: EventType.TEXT_MESSAGE_CONTENT,
30
+ messageId: "",
31
+ delta,
32
+ agentId
33
+ };
34
+ }
35
+ next = await reader.next();
36
+ }
37
+ const finalObject = await streamResult.output;
38
+ if (finalObject === null) {
39
+ yield {
40
+ type: EventType.RUN_ERROR,
41
+ message: "Anthropic stream completed with null output",
42
+ agentId
43
+ };
44
+ return;
45
+ }
46
+ const validated = schema.parse(finalObject);
47
+ yield {
48
+ type: EventType.TOOL_CALL_RESULT,
49
+ toolCallId: "",
50
+ messageId: "",
51
+ content: JSON.stringify(validated),
52
+ agentId
53
+ };
54
+ } catch (err) {
55
+ yield {
56
+ type: EventType.RUN_ERROR,
57
+ message: `Anthropic stream error: ${err.message}`,
58
+ agentId
59
+ };
60
+ }
61
+ }
62
+ function extractDelta(partial, prevLength) {
63
+ const chatbotMessage = partial.chatbotMessage;
64
+ if (typeof chatbotMessage !== "string" || chatbotMessage.length <= prevLength) {
65
+ return null;
66
+ }
67
+ return chatbotMessage.slice(prevLength);
68
+ }
69
+
70
+ // utils/executeWithFallback.ts
71
+ async function executeWithFallback(models, action) {
72
+ for (let i = 0; i < models.length; i++) {
73
+ const model = models[i];
74
+ try {
75
+ return await action(model);
76
+ } catch (error) {
77
+ const errorObj = error;
78
+ if (i === models.length - 1) {
79
+ throw errorObj;
80
+ }
81
+ }
82
+ }
83
+ throw new Error("All models failed");
84
+ }
85
+
86
+ // anthropic/index.ts
87
+ function toAIMessages(messages) {
88
+ return messages.map((msg) => ({
89
+ role: msg.role,
90
+ content: msg.content
91
+ }));
92
+ }
93
+ function prepareMessages(messages) {
94
+ if (messages.length === 0) return messages;
95
+ const lastMessage = messages[messages.length - 1];
96
+ if (lastMessage.role === "assistant") {
97
+ return [...messages, { role: "user", content: "Continue" }];
98
+ }
99
+ return messages;
100
+ }
101
+ async function sendWithModel(anthropicProvider, model, system, messages, schema) {
102
+ const preparedMessages = prepareMessages(messages);
103
+ const result = await generateText({
104
+ model: anthropicProvider(model),
105
+ system,
106
+ messages: preparedMessages,
107
+ output: Output.object({ schema })
108
+ });
109
+ return {
110
+ content: JSON.stringify(result.output),
111
+ usage: {
112
+ inputTokens: result.usage.inputTokens ?? 0,
113
+ outputTokens: result.usage.outputTokens ?? 0,
114
+ totalTokens: (result.usage.inputTokens ?? 0) + (result.usage.outputTokens ?? 0)
115
+ }
116
+ };
117
+ }
118
+ async function sendStreamWithModel(anthropicProvider, model, system, messages, schema) {
119
+ const preparedMessages = prepareMessages(messages);
120
+ const result = streamText({
121
+ model: anthropicProvider(model),
122
+ system,
123
+ messages: preparedMessages,
124
+ output: Output.object({ schema })
125
+ });
126
+ const partialStream = result.partialOutputStream;
127
+ const reader = partialStream[Symbol.asyncIterator]();
128
+ const first = await reader.next();
129
+ return { result, reader, first };
130
+ }
131
+ function createAnthropicProvider(config) {
132
+ const anthropicProvider = createAnthropic({
133
+ apiKey: config.apiKey,
134
+ baseURL: config.baseURL
135
+ });
136
+ const models = config.models;
137
+ return {
138
+ name: "anthropic",
139
+ async sendRequest(request) {
140
+ const messages = toAIMessages(request.messages);
141
+ return executeWithFallback(
142
+ models,
143
+ (model) => sendWithModel(anthropicProvider, model, request.systemPrompt, messages, request.responseSchema)
144
+ );
145
+ },
146
+ async *sendRequestStream(request) {
147
+ const messages = toAIMessages(request.messages);
148
+ const { result, reader, first } = await executeWithFallback(
149
+ models,
150
+ (model) => sendStreamWithModel(anthropicProvider, model, request.systemPrompt, messages, request.responseSchema)
151
+ );
152
+ yield* processAnthropicStream(result, reader, first, "anthropic", request.responseSchema);
153
+ }
154
+ };
155
+ }
156
+
157
+ export { createAnthropicProvider, prepareMessages };
158
+ //# sourceMappingURL=index.js.map
159
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../anthropic/streamProcessor.ts","../../utils/executeWithFallback.ts","../../anthropic/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { EventType } from '@ag-ui/client'\nimport { ZodType } from 'zod'\nimport type { AgentId, StreamEvent, BaseState } from '@genui-a3/core'\nimport type { StreamTextResult, ToolSet } from 'ai'\n\n/**\n * Processes an Anthropic streaming response (via Vercel AI SDK) into AG-UI events.\n *\n * Uses `partialOutputStream` from `streamText` + `Output.object()` to receive\n * progressively-built partial objects. Tracks `chatbotMessage` growth to yield\n * TEXT_MESSAGE_CONTENT deltas. After the stream completes, validates the final\n * object and yields TOOL_CALL_RESULT.\n *\n * @param streamResult - The streamText result containing partialOutputStream and output promise\n * @param reader - Pre-started async iterator for the partial object stream\n * @param first - The first iteration result (already consumed to trigger the API call)\n * @param agentId - Agent identifier for event tagging\n * @param schema - Zod schema for final response validation\n * @returns Async generator of AG-UI stream events\n */\nexport async function* processAnthropicStream<TState extends BaseState = BaseState>(\n streamResult: StreamTextResult<ToolSet, never>,\n reader: AsyncIterator<unknown>,\n first: IteratorResult<unknown>,\n agentId: AgentId,\n schema: ZodType,\n): AsyncGenerator<StreamEvent<TState>> {\n let prevMessageLength = 0\n\n try {\n // Process the first partial (already consumed to trigger the API call)\n if (!first.done) {\n const partial = first.value as Record<string, unknown>\n const delta = extractDelta(partial, prevMessageLength)\n if (delta) {\n prevMessageLength += delta.length\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: '',\n delta,\n agentId,\n } as StreamEvent<TState>\n }\n }\n\n // Process remaining partials\n let next = await reader.next()\n while (!next.done) {\n const partial = next.value as Record<string, unknown>\n const delta = extractDelta(partial, prevMessageLength)\n if (delta) {\n prevMessageLength += delta.length\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: '',\n delta,\n agentId,\n } as StreamEvent<TState>\n }\n // eslint-disable-next-line no-await-in-loop\n next = await reader.next()\n }\n\n // Stream complete — await and validate the final object\n const finalObject = await streamResult.output\n\n if (finalObject === null) {\n yield {\n type: EventType.RUN_ERROR,\n message: 'Anthropic stream completed with null output',\n agentId,\n } as StreamEvent<TState>\n return\n }\n\n const validated = schema.parse(finalObject)\n yield {\n type: EventType.TOOL_CALL_RESULT,\n toolCallId: '',\n messageId: '',\n content: JSON.stringify(validated),\n agentId,\n } as StreamEvent<TState>\n } catch (err) {\n yield {\n type: EventType.RUN_ERROR,\n message: `Anthropic stream error: ${(err as Error).message}`,\n agentId,\n } as StreamEvent<TState>\n }\n}\n\n/**\n * Extracts the new portion of chatbotMessage from a partial object.\n */\nfunction extractDelta(partial: Record<string, unknown>, prevLength: number): string | null {\n const chatbotMessage = partial.chatbotMessage\n if (typeof chatbotMessage !== 'string' || chatbotMessage.length <= prevLength) {\n return null\n }\n return chatbotMessage.slice(prevLength)\n}\n","/**\n * Executes an action with model fallback support.\n * Tries each model in order; if one fails, falls back to the next.\n * Throws the last error if all models fail.\n *\n * @param models - Model identifiers in priority order\n * @param action - Async action to attempt with each model\n * @returns The result from the first successful model\n * @throws The error from the last model if all fail\n *\n * @example\n * ```typescript\n * const result = await executeWithFallback(\n * ['model-primary', 'model-fallback'],\n * (model) => provider.call(model, params),\n * )\n * ```\n */\nexport async function executeWithFallback<T>(models: string[], action: (model: string) => Promise<T>): Promise<T> {\n const errors: Array<{ model: string; error: Error }> = []\n\n for (let i = 0; i < models.length; i++) {\n const model = models[i]\n\n try {\n // eslint-disable-next-line no-await-in-loop\n return await action(model)\n } catch (error) {\n const errorObj = error as Error\n errors.push({ model, error: errorObj })\n\n if (i === models.length - 1) {\n throw errorObj\n }\n }\n }\n\n throw new Error('All models failed')\n}\n","import { createAnthropic } from '@ai-sdk/anthropic'\nimport { generateText, streamText, Output, ModelMessage } from 'ai'\nimport type {\n Provider,\n ProviderRequest,\n ProviderResponse,\n ProviderMessage,\n BaseState,\n StreamEvent,\n} from '@genui-a3/core'\nimport { processAnthropicStream } from './streamProcessor'\nimport { executeWithFallback } from '../utils/executeWithFallback'\n\n/**\n * Configuration for creating an Anthropic provider.\n */\nexport interface AnthropicProviderConfig {\n /** Anthropic API key. Defaults to ANTHROPIC_API_KEY env var. */\n apiKey?: string\n /**\n * Model identifiers in order of preference (first = primary, rest = fallbacks).\n * e.g. ['claude-sonnet-4-5-20250929', 'claude-haiku-4-5-20251001']\n */\n models: string[]\n /** Optional custom base URL for the Anthropic API */\n baseURL?: string\n}\n\nfunction toAIMessages(messages: ProviderMessage[]): ModelMessage[] {\n return messages.map((msg) => ({\n role: msg.role,\n content: msg.content,\n }))\n}\n\nexport function prepareMessages(messages: ModelMessage[]): ModelMessage[] {\n if (messages.length === 0) return messages\n const lastMessage = messages[messages.length - 1]\n if (lastMessage.role === 'assistant') {\n return [...messages, { role: 'user', content: 'Continue' }]\n }\n return messages\n}\n\nasync function sendWithModel(\n anthropicProvider: ReturnType<typeof createAnthropic>,\n model: string,\n system: string,\n messages: ModelMessage[],\n schema: ProviderRequest['responseSchema'],\n): Promise<ProviderResponse> {\n const preparedMessages = prepareMessages(messages)\n const result = await generateText({\n model: anthropicProvider(model),\n system,\n messages: preparedMessages,\n output: Output.object({ schema }),\n })\n\n return {\n content: JSON.stringify(result.output),\n usage: {\n inputTokens: result.usage.inputTokens ?? 0,\n outputTokens: result.usage.outputTokens ?? 0,\n totalTokens: (result.usage.inputTokens ?? 0) + (result.usage.outputTokens ?? 0),\n },\n }\n}\n\nasync function sendStreamWithModel(\n anthropicProvider: ReturnType<typeof createAnthropic>,\n model: string,\n system: string,\n messages: ModelMessage[],\n schema: ProviderRequest['responseSchema'],\n) {\n const preparedMessages = prepareMessages(messages)\n const result = streamText({\n model: anthropicProvider(model),\n system,\n messages: preparedMessages,\n output: Output.object({ schema }),\n })\n\n // Force the API call to start so executeWithFallback can catch connection errors\n const partialStream = result.partialOutputStream\n const reader = partialStream[Symbol.asyncIterator]()\n const first = await reader.next()\n\n return { result, reader, first }\n}\n\n/**\n * Creates an Anthropic provider instance.\n *\n * Uses the Vercel AI SDK (`ai` + `@ai-sdk/anthropic`) for structured output via\n * `generateText` + `Output.object()` (blocking) and `streamText` + `Output.object()`\n * (streaming). The AI SDK handles Zod-to-JSON-schema conversion, partial JSON\n * parsing, and validation internally.\n *\n * @param config - Anthropic provider configuration\n * @returns A Provider implementation using Anthropic\n *\n * @example\n * ```typescript\n * const provider = createAnthropicProvider({\n * models: ['claude-sonnet-4-5-20250929', 'claude-haiku-4-5-20251001'],\n * })\n * ```\n */\nexport function createAnthropicProvider(config: AnthropicProviderConfig): Provider {\n const anthropicProvider = createAnthropic({\n apiKey: config.apiKey,\n baseURL: config.baseURL,\n })\n const models = config.models\n\n return {\n name: 'anthropic',\n\n async sendRequest(request: ProviderRequest): Promise<ProviderResponse> {\n const messages = toAIMessages(request.messages)\n\n return executeWithFallback(models, (model) =>\n sendWithModel(anthropicProvider, model, request.systemPrompt, messages, request.responseSchema),\n )\n },\n\n async *sendRequestStream<TState extends BaseState = BaseState>(\n request: ProviderRequest,\n ): AsyncGenerator<StreamEvent<TState>> {\n const messages = toAIMessages(request.messages)\n\n const { result, reader, first } = await executeWithFallback(models, (model) =>\n sendStreamWithModel(anthropicProvider, model, request.systemPrompt, messages, request.responseSchema),\n )\n\n yield* processAnthropicStream<TState>(result, reader, first, 'anthropic', request.responseSchema)\n },\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genui-a3/providers",
3
- "version": "0.0.0",
3
+ "version": "0.0.2",
4
4
  "description": "Provider implementations for the A3 agentic framework",
5
5
  "type": "module",
6
6
  "exports": {
@@ -13,6 +13,11 @@
13
13
  "types": "./dist/openai/index.d.ts",
14
14
  "import": "./dist/openai/index.js",
15
15
  "require": "./dist/openai/index.cjs"
16
+ },
17
+ "./anthropic": {
18
+ "types": "./dist/anthropic/index.d.ts",
19
+ "import": "./dist/anthropic/index.js",
20
+ "require": "./dist/anthropic/index.cjs"
16
21
  }
17
22
  },
18
23
  "files": [
@@ -29,6 +34,7 @@
29
34
  "providers",
30
35
  "bedrock",
31
36
  "openai",
37
+ "anthropic",
32
38
  "ai",
33
39
  "llm"
34
40
  ],
@@ -42,7 +48,9 @@
42
48
  },
43
49
  "dependencies": {
44
50
  "@ag-ui/client": "0.0.47",
51
+ "@ai-sdk/anthropic": "^3.0.58",
45
52
  "@aws-sdk/client-bedrock-runtime": "3.975.0",
53
+ "ai": "^6.0.116",
46
54
  "openai": "6.27.0",
47
55
  "zod": "4.3.6"
48
56
  },