@genui-a3/providers 0.0.1 → 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.
|
@@ -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.
|
|
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
|
},
|