@inneranimalmedia/agentsam-sdk 2.6.0 → 2.6.1
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/docs/PLATFORM_RUNTIME_EVENTS.md +48 -0
- package/docs/RELEASES.md +7 -7
- package/docs/SOURCE_ARCHITECTURE.md +58 -0
- package/docs/TEST_TIERS.md +26 -0
- package/migrations/runtime/0001_cli_runtime.sql +298 -0
- package/package.json +28 -7
- package/packages/agentsam-repository/README.md +15 -0
- package/packages/agentsam-repository/package.json +25 -0
- package/packages/agentsam-repository/src/contracts.js +113 -0
- package/packages/agentsam-repository/src/index.js +3 -0
- package/{src/lib → packages/agentsam-repository/src}/merkle/cloudflare-persistence.js +14 -24
- package/{src/lib → packages/agentsam-repository/src}/merkle/index.js +1 -0
- package/{src/lib → packages/agentsam-repository/src}/merkle/persistence.js +6 -4
- package/{src/lib → packages/agentsam-repository/src}/merkle/policy.js +1 -0
- package/packages/agentsam-repository/test/contracts.test.mjs +40 -0
- package/packages/agentsam-repository/test/git-context.test.mjs +24 -0
- package/{test/merkle.test.mjs → packages/agentsam-repository/test/merkle-core.test.mjs} +2 -32
- package/{test → packages/agentsam-repository/test}/merkle-persistence.test.mjs +11 -6
- package/packages/identity/package.json +1 -1
- package/protocol/COMPANY_REPOSITORY_GRAPH_V1.md +91 -0
- package/protocol/MERKLE_PERSISTENCE_V1.md +2 -0
- package/protocol/MERKLE_PERSISTENCE_V2.md +40 -0
- package/protocol/repository/repository-contract.schema.json +24 -0
- package/protocol/repository/repository-dependency.schema.json +24 -0
- package/protocol/repository/repository-identity.schema.json +17 -0
- package/protocol/rpc/v1/common.proto +16 -0
- package/protocol/rpc/v1/errors.proto +35 -0
- package/protocol/rpc/v1/knowledge.proto +77 -0
- package/services/knowledge/package-lock.json +333 -0
- package/services/knowledge/package.json +5 -1
- package/src/agent/responses-runner.js +63 -35
- package/src/capabilities/repository-snapshot.js +3 -3
- package/src/cli.js +23 -6
- package/src/commands/context-economics.js +17 -2
- package/src/commands/context.js +1 -1
- package/src/commands/db.js +20 -3
- package/src/commands/env.js +90 -0
- package/src/commands/knowledge.js +12 -4
- package/src/commands/merkle-persist.js +30 -11
- package/src/commands/merkle.js +1 -1
- package/src/commands/models.js +123 -65
- package/src/commands/ollama.js +26 -0
- package/src/commands/preferences.js +53 -26
- package/src/commands/shell.js +236 -48
- package/src/errors/contract.js +236 -0
- package/src/errors/index.js +14 -0
- package/src/index.js +13 -1
- package/src/knowledge/service/auth.js +13 -0
- package/src/knowledge/service/grpc-client.js +115 -0
- package/src/knowledge/service/grpc-codec.js +237 -0
- package/src/knowledge/service/grpc-server.js +83 -0
- package/src/knowledge/service/job-engine.js +248 -0
- package/src/knowledge/service/server.js +87 -135
- package/src/knowledge/source.js +1 -1
- package/src/lib/cli-preferences.js +31 -4
- package/src/lib/deploy-receipt/index.js +2 -2
- package/src/lib/knowledge-docker.js +6 -3
- package/src/lib/local-sessions.js +23 -2
- package/src/lib/local-status.js +1 -1
- package/src/lib/project-config.js +1 -1
- package/src/lib/provider-credentials.js +105 -5
- package/src/lib/slash-commands.js +4 -3
- package/src/local/migrations.js +93 -0
- package/src/local/runtime-store.js +141 -0
- package/src/local/sqlite.js +2 -0
- package/src/local-pty/server.js +113 -51
- package/src/models/discovery.js +292 -0
- package/src/providers/anthropic-messages.js +192 -0
- package/src/providers/cloudflare-chat.js +183 -0
- package/src/providers/factory.js +69 -0
- package/src/providers/gemini-generate-content.js +208 -0
- package/src/providers/index.js +5 -0
- package/src/providers/ollama-chat.js +148 -0
- package/src/providers/openai-responses.js +226 -75
- package/src/repository/index.js +14 -2
- package/src/rpc/generated/common_grpc_pb.js +1 -0
- package/src/rpc/generated/common_pb.js +536 -0
- package/src/rpc/generated/errors_grpc_pb.js +1 -0
- package/src/rpc/generated/errors_pb.js +482 -0
- package/src/rpc/generated/knowledge_grpc_pb.js +135 -0
- package/src/rpc/generated/knowledge_pb.js +2168 -0
- package/src/rpc/generated/package.json +3 -0
- package/src/security/trust-boundary.js +2 -2
- package/src/telemetry/events.js +4 -1
- package/src/ui/cli/activity.js +76 -0
- package/src/ui/cli/compaction.js +15 -0
- package/src/ui/cli/footer.js +39 -0
- package/src/ui/cli/help.js +192 -0
- package/src/ui/cli/plan.js +20 -0
- package/src/ui/cli/runtime-events.js +110 -0
- package/src/ui/cli/waiting.js +16 -0
- package/src/ui/merkle/render.js +1 -1
- package/test/cli/preferences-runtime.test.mjs +11 -0
- package/test/cli/runtime-ui.test.mjs +74 -0
- package/test/error-diagnostics.test.mjs +57 -1
- package/test/fixtures/knowledge-rpc-worker.mjs +16 -0
- package/test/integration/cli-help.test.mjs +37 -0
- package/test/integration/knowledge-rpc.test.mjs +112 -0
- package/test/integration/merkle-cli.test.mjs +61 -0
- package/test/integration/merkle-persistence-identity.test.mjs +48 -0
- package/test/integration/provider-env-cli.test.mjs +49 -0
- package/test/integration/provider-factory.test.mjs +197 -0
- package/test/integration/repository-company-graph.test.mjs +90 -0
- package/test/integration/runtime-migrations.test.mjs +82 -0
- package/test/knowledge-service.test.mjs +5 -0
- package/test/knowledge.test.mjs +16 -0
- package/test/live/terminal-transport.live.test.mjs +24 -0
- package/test/local-sessions.test.mjs +7 -1
- package/test/models.test.mjs +101 -4
- package/test/ollama.test.mjs +21 -0
- package/test/portable-context.test.mjs +1 -1
- package/test/provider-credentials.test.mjs +45 -1
- package/test/release-hygiene.test.mjs +13 -5
- package/test/responses-runner.test.mjs +3 -1
- package/test/shell.test.mjs +50 -8
- package/test/terminal/local-pty.mock.test.mjs +151 -0
- /package/{src/lib → packages/agentsam-repository/src}/git-context.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/diff.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/filemeta.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/git-ignore.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/hash.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/semantic.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/snapshot.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/tree.js +0 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { createAgentEvent, createUsageSnapshot } from '../telemetry/index.js';
|
|
2
|
+
import { calculateModelCost } from '../models/index.js';
|
|
3
|
+
import { diagnosticFromError } from '../errors/index.js';
|
|
4
|
+
|
|
5
|
+
function clean(value) { return value == null ? '' : String(value).trim(); }
|
|
6
|
+
function integer(value) { const n = Number(value ?? 0); return Number.isFinite(n) && n >= 0 ? Math.floor(n) : 0; }
|
|
7
|
+
function emit(emitFn, type, payload, runId) {
|
|
8
|
+
if (typeof emitFn === 'function') emitFn(createAgentEvent(type, payload, { runId }));
|
|
9
|
+
}
|
|
10
|
+
function signal(ms) {
|
|
11
|
+
return typeof AbortSignal?.timeout === 'function' && Number(ms) > 0 ? AbortSignal.timeout(Number(ms)) : undefined;
|
|
12
|
+
}
|
|
13
|
+
function toolsForAnthropic(tools = []) {
|
|
14
|
+
return tools.map((tool) => ({
|
|
15
|
+
name: tool.name,
|
|
16
|
+
description: tool.description || '',
|
|
17
|
+
input_schema: tool.parameters || { type: 'object', properties: {} },
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
function textFromContent(content = []) {
|
|
21
|
+
return content.filter((part) => part?.type === 'text').map((part) => String(part.text || '')).join('');
|
|
22
|
+
}
|
|
23
|
+
function callsFromContent(content = []) {
|
|
24
|
+
return content.filter((part) => part?.type === 'tool_use').map((part) => ({
|
|
25
|
+
call_id: part.id,
|
|
26
|
+
name: part.name,
|
|
27
|
+
arguments: JSON.stringify(part.input || {}),
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
function usageParts(response = {}) {
|
|
31
|
+
const usage = response.usage || {};
|
|
32
|
+
return {
|
|
33
|
+
input_tokens: integer(usage.input_tokens),
|
|
34
|
+
cached_input_tokens: integer(usage.cache_read_input_tokens),
|
|
35
|
+
cache_write_tokens: integer(usage.cache_creation_input_tokens),
|
|
36
|
+
output_tokens: integer(usage.output_tokens),
|
|
37
|
+
reasoning_tokens: 0,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function cumulative(base, delta) {
|
|
41
|
+
return {
|
|
42
|
+
input_tokens: integer(base?.input_tokens) + delta.input_tokens,
|
|
43
|
+
output_tokens: integer(base?.output_tokens) + delta.output_tokens,
|
|
44
|
+
cached_input_tokens: integer(base?.cached_input_tokens) + delta.cached_input_tokens,
|
|
45
|
+
cache_write_tokens: integer(base?.cache_write_tokens) + delta.cache_write_tokens,
|
|
46
|
+
reasoning_tokens: integer(base?.reasoning_tokens),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function createAnthropicMessagesAdapter(options = {}) {
|
|
51
|
+
const apiKey = clean(options.apiKey || process.env.ANTHROPIC_API_KEY);
|
|
52
|
+
const fetchImpl = options.fetchImpl || fetch;
|
|
53
|
+
const baseUrl = clean(options.baseUrl || 'https://api.anthropic.com/v1').replace(/\/$/, '');
|
|
54
|
+
|
|
55
|
+
async function request(body, params = {}) {
|
|
56
|
+
if (!apiKey) throw new Error('anthropic_api_key_required');
|
|
57
|
+
const response = await fetchImpl(baseUrl + '/messages', {
|
|
58
|
+
method: 'POST',
|
|
59
|
+
headers: {
|
|
60
|
+
'x-api-key': apiKey,
|
|
61
|
+
'anthropic-version': '2023-06-01',
|
|
62
|
+
'content-type': 'application/json',
|
|
63
|
+
},
|
|
64
|
+
body: JSON.stringify(body),
|
|
65
|
+
signal: signal(params.timeoutMs || options.timeoutMs),
|
|
66
|
+
});
|
|
67
|
+
const data = await response.json().catch(() => ({}));
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
const error = new Error(data?.error?.message || data?.message || `Anthropic HTTP ${response.status}`);
|
|
70
|
+
error.status = response.status;
|
|
71
|
+
error.provider = 'anthropic';
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
return data;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function send(messages, params = {}) {
|
|
78
|
+
const record = params.modelRecord || options.modelRecord;
|
|
79
|
+
if (!record || record.provider !== 'anthropic') throw new Error('anthropic_model_record_required');
|
|
80
|
+
const emitFn = params.emit || options.emit;
|
|
81
|
+
const runId = params.runId;
|
|
82
|
+
const model = record.provider_model_id;
|
|
83
|
+
const body = {
|
|
84
|
+
model,
|
|
85
|
+
max_tokens: Number(params.maxOutputTokens) > 0 ? Number(params.maxOutputTokens) : 8192,
|
|
86
|
+
messages,
|
|
87
|
+
...(clean(params.instructions) ? {
|
|
88
|
+
system: [{
|
|
89
|
+
type: 'text',
|
|
90
|
+
text: String(params.instructions),
|
|
91
|
+
cache_control: { type: 'ephemeral' },
|
|
92
|
+
}],
|
|
93
|
+
} : {}),
|
|
94
|
+
...(params.tools?.length ? { tools: toolsForAnthropic(params.tools) } : {}),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
emit(emitFn, 'model.started', {
|
|
98
|
+
provider: 'anthropic',
|
|
99
|
+
model,
|
|
100
|
+
reasoning_effort: params.reasoningEffort || 'auto',
|
|
101
|
+
requested_service_tier: 'default',
|
|
102
|
+
}, runId);
|
|
103
|
+
|
|
104
|
+
let response;
|
|
105
|
+
try {
|
|
106
|
+
response = await request(body, params);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
const diagnostic = diagnosticFromError(error, { source: 'anthropic', kind: 'provider_error' });
|
|
109
|
+
emit(emitFn, 'error.observed', diagnostic, runId);
|
|
110
|
+
emit(emitFn, 'run.failed', { stage: 'model', provider: 'anthropic', model, error: diagnostic }, runId);
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const delta = usageParts(response);
|
|
115
|
+
const usageSnapshot = createUsageSnapshot({
|
|
116
|
+
current_context: { input_tokens: delta.input_tokens, window_tokens: Number(record.context_window) || 0 },
|
|
117
|
+
cumulative: cumulative(params.cumulativeUsage, delta),
|
|
118
|
+
estimate_kind: 'provider',
|
|
119
|
+
});
|
|
120
|
+
const cost = record.pricing ? calculateModelCost(record, delta, { serviceTier: 'default' }) : null;
|
|
121
|
+
if (cost) emit(emitFn, 'cost.snapshot', cost, runId);
|
|
122
|
+
emit(emitFn, 'usage.snapshot', usageSnapshot, runId);
|
|
123
|
+
emit(emitFn, 'model.completed', {
|
|
124
|
+
provider: 'anthropic',
|
|
125
|
+
model,
|
|
126
|
+
response_id: response.id || null,
|
|
127
|
+
status: response.stop_reason || 'completed',
|
|
128
|
+
requested_service_tier: 'default',
|
|
129
|
+
actual_service_tier: 'default',
|
|
130
|
+
}, runId);
|
|
131
|
+
|
|
132
|
+
const assistant = { role: 'assistant', content: response.content || [] };
|
|
133
|
+
const nextMessages = [...messages, assistant];
|
|
134
|
+
return Object.freeze({
|
|
135
|
+
provider: 'anthropic',
|
|
136
|
+
model,
|
|
137
|
+
response_id: response.id || null,
|
|
138
|
+
status: response.stop_reason || 'completed',
|
|
139
|
+
output_text: textFromContent(response.content),
|
|
140
|
+
tool_calls: Object.freeze(callsFromContent(response.content)),
|
|
141
|
+
usage_delta: Object.freeze(delta),
|
|
142
|
+
usage_snapshot: usageSnapshot,
|
|
143
|
+
cost,
|
|
144
|
+
provider_state: Object.freeze({ messages: nextMessages }),
|
|
145
|
+
requested_service_tier: 'default',
|
|
146
|
+
actual_service_tier: 'default',
|
|
147
|
+
raw: response,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function create(params = {}) {
|
|
152
|
+
const prior = Array.isArray(params.providerState?.messages) ? params.providerState.messages : [];
|
|
153
|
+
const input = typeof params.input === 'string' ? params.input : JSON.stringify(params.input ?? '');
|
|
154
|
+
const messages = [...prior, { role: 'user', content: [{ type: 'text', text: input }] }];
|
|
155
|
+
return send(messages, params);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function continueWithToolOutputs(params = {}) {
|
|
159
|
+
const prior = Array.isArray(params.providerState?.messages) ? params.providerState.messages : [];
|
|
160
|
+
if (!prior.length) throw new Error('anthropic_provider_state_required');
|
|
161
|
+
const content = (params.toolOutputs || []).map((row) => ({
|
|
162
|
+
type: 'tool_result',
|
|
163
|
+
tool_use_id: row.call_id || row.callId,
|
|
164
|
+
content: typeof row.output === 'string' ? row.output : JSON.stringify(row.output ?? null),
|
|
165
|
+
}));
|
|
166
|
+
if (!content.length) throw new Error('anthropic_tool_outputs_required');
|
|
167
|
+
return send([...prior, { role: 'user', content }], params);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function compact(params = {}) {
|
|
171
|
+
const prior = Array.isArray(params.providerState?.messages) ? params.providerState.messages : [];
|
|
172
|
+
if (!prior.length) throw new Error('anthropic_provider_state_required');
|
|
173
|
+
const emitFn = params.emit || options.emit;
|
|
174
|
+
const startedAt = Date.now();
|
|
175
|
+
emit(emitFn, 'context.compaction.started', { provider: 'anthropic', model: params.model }, params.runId);
|
|
176
|
+
const summary = await send([...prior, {
|
|
177
|
+
role: 'user',
|
|
178
|
+
content: [{ type: 'text', text: 'Create a compact continuation summary of this conversation. Preserve the objective, decisions, changed files, active plan, unresolved failures, tool results that still matter, and constraints. Do not add new work.' }],
|
|
179
|
+
}], { ...params, tools: [], maxOutputTokens: Math.min(Number(params.maxOutputTokens) || 4096, 4096) });
|
|
180
|
+
const summaryText = String(summary.output_text || '').trim();
|
|
181
|
+
const compactState = Object.freeze({ messages: [{ role: 'user', content: [{ type: 'text', text: `Prior conversation continuation summary:\n${summaryText}` }] }] });
|
|
182
|
+
emit(emitFn, 'context.compaction.completed', {
|
|
183
|
+
provider: 'anthropic', model: params.model, summary_text: summaryText,
|
|
184
|
+
tokens_before: params.tokensBefore ?? null,
|
|
185
|
+
tokens_after: Math.ceil(summaryText.length / 4),
|
|
186
|
+
duration_ms: Date.now() - startedAt,
|
|
187
|
+
}, params.runId);
|
|
188
|
+
return Object.freeze({ provider: 'anthropic', output: Object.freeze([]), provider_state: compactState, summary_text: summaryText, usage: summary.usage_delta || null });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return Object.freeze({ provider: 'anthropic', create, continueWithToolOutputs, compact });
|
|
192
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { createAgentEvent, createUsageSnapshot } from '../telemetry/index.js';
|
|
2
|
+
import { diagnosticFromError } from '../errors/index.js';
|
|
3
|
+
|
|
4
|
+
function clean(value) { return value == null ? '' : String(value).trim(); }
|
|
5
|
+
function integer(value) { const n = Number(value ?? 0); return Number.isFinite(n) && n >= 0 ? Math.floor(n) : 0; }
|
|
6
|
+
function emit(emitFn, type, payload, runId) {
|
|
7
|
+
if (typeof emitFn === 'function') emitFn(createAgentEvent(type, payload, { runId }));
|
|
8
|
+
}
|
|
9
|
+
function toolsForChat(tools = []) {
|
|
10
|
+
return tools.map((tool) => ({
|
|
11
|
+
type: 'function',
|
|
12
|
+
function: {
|
|
13
|
+
name: tool.name,
|
|
14
|
+
description: tool.description || '',
|
|
15
|
+
parameters: tool.parameters || { type: 'object', properties: {} },
|
|
16
|
+
},
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
19
|
+
function usageParts(response = {}) {
|
|
20
|
+
const usage = response.usage || {};
|
|
21
|
+
return {
|
|
22
|
+
input_tokens: integer(usage.prompt_tokens),
|
|
23
|
+
cached_input_tokens: integer(usage.prompt_tokens_details?.cached_tokens),
|
|
24
|
+
cache_write_tokens: 0,
|
|
25
|
+
output_tokens: integer(usage.completion_tokens),
|
|
26
|
+
reasoning_tokens: integer(usage.completion_tokens_details?.reasoning_tokens),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function cumulative(base, delta) {
|
|
30
|
+
return {
|
|
31
|
+
input_tokens: integer(base?.input_tokens) + delta.input_tokens,
|
|
32
|
+
output_tokens: integer(base?.output_tokens) + delta.output_tokens,
|
|
33
|
+
cached_input_tokens: integer(base?.cached_input_tokens) + delta.cached_input_tokens,
|
|
34
|
+
cache_write_tokens: integer(base?.cache_write_tokens),
|
|
35
|
+
reasoning_tokens: integer(base?.reasoning_tokens) + delta.reasoning_tokens,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function toolCalls(message = {}) {
|
|
39
|
+
return (message.tool_calls || []).map((call) => ({
|
|
40
|
+
call_id: call.id,
|
|
41
|
+
name: call.function?.name,
|
|
42
|
+
arguments: typeof call.function?.arguments === 'string'
|
|
43
|
+
? call.function.arguments
|
|
44
|
+
: JSON.stringify(call.function?.arguments || {}),
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function createCloudflareChatAdapter(options = {}) {
|
|
49
|
+
const credential = options.credential || {};
|
|
50
|
+
const apiToken = clean(credential.value || process.env.CLOUDFLARE_API_TOKEN);
|
|
51
|
+
const accountId = clean(credential.account_id || process.env.CLOUDFLARE_ACCOUNT_ID || process.env.ACCOUNT_ID);
|
|
52
|
+
const gatewayId = clean(options.gatewayId || process.env.CLOUDFLARE_AI_GATEWAY_ID || 'default');
|
|
53
|
+
const fetchImpl = options.fetchImpl || fetch;
|
|
54
|
+
|
|
55
|
+
async function request(body, params = {}) {
|
|
56
|
+
if (!apiToken) throw new Error('cloudflare_api_token_required');
|
|
57
|
+
if (!accountId) throw new Error('cloudflare_account_id_required');
|
|
58
|
+
const url = `https://api.cloudflare.com/client/v4/accounts/${encodeURIComponent(accountId)}/ai/v1/chat/completions`;
|
|
59
|
+
const response = await fetchImpl(url, {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: {
|
|
62
|
+
authorization: `Bearer ${apiToken}`,
|
|
63
|
+
'content-type': 'application/json',
|
|
64
|
+
'cf-aig-gateway-id': gatewayId,
|
|
65
|
+
},
|
|
66
|
+
body: JSON.stringify(body),
|
|
67
|
+
signal: typeof AbortSignal?.timeout === 'function' && Number(params.timeoutMs || options.timeoutMs) > 0
|
|
68
|
+
? AbortSignal.timeout(Number(params.timeoutMs || options.timeoutMs))
|
|
69
|
+
: undefined,
|
|
70
|
+
});
|
|
71
|
+
const data = await response.json().catch(() => ({}));
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
const error = new Error(data?.error?.message || data?.errors?.[0]?.message || data?.message || `Cloudflare Workers AI HTTP ${response.status}`);
|
|
74
|
+
error.status = response.status;
|
|
75
|
+
error.provider = 'cloudflare';
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
return data;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function send(messages, params = {}) {
|
|
82
|
+
const record = params.modelRecord || options.modelRecord;
|
|
83
|
+
if (!record || record.provider !== 'cloudflare') throw new Error('cloudflare_model_record_required');
|
|
84
|
+
const emitFn = params.emit || options.emit;
|
|
85
|
+
const model = record.provider_model_id;
|
|
86
|
+
const tools = toolsForChat(params.tools || []);
|
|
87
|
+
emit(emitFn, 'model.started', { provider: 'cloudflare', model, reasoning_effort: 'auto', requested_service_tier: 'default' }, params.runId);
|
|
88
|
+
|
|
89
|
+
let response;
|
|
90
|
+
try {
|
|
91
|
+
response = await request({
|
|
92
|
+
model,
|
|
93
|
+
messages,
|
|
94
|
+
...(tools.length ? { tools, tool_choice: 'auto' } : {}),
|
|
95
|
+
...(Number(params.maxOutputTokens) > 0 ? { max_tokens: Number(params.maxOutputTokens) } : {}),
|
|
96
|
+
}, params);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
const diagnostic = diagnosticFromError(error, { source: 'cloudflare', kind: 'provider_error' });
|
|
99
|
+
emit(emitFn, 'error.observed', diagnostic, params.runId);
|
|
100
|
+
emit(emitFn, 'run.failed', { stage: 'model', provider: 'cloudflare', model, error: diagnostic }, params.runId);
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const message = response?.choices?.[0]?.message || { role: 'assistant', content: '' };
|
|
105
|
+
const delta = usageParts(response);
|
|
106
|
+
const usageSnapshot = createUsageSnapshot({
|
|
107
|
+
current_context: { input_tokens: delta.input_tokens, window_tokens: Number(record.context_window) || 0 },
|
|
108
|
+
cumulative: cumulative(params.cumulativeUsage, delta),
|
|
109
|
+
estimate_kind: 'provider',
|
|
110
|
+
});
|
|
111
|
+
emit(emitFn, 'usage.snapshot', usageSnapshot, params.runId);
|
|
112
|
+
emit(emitFn, 'model.completed', {
|
|
113
|
+
provider: 'cloudflare', model, response_id: response.id || null,
|
|
114
|
+
status: response?.choices?.[0]?.finish_reason || 'completed',
|
|
115
|
+
requested_service_tier: 'default', actual_service_tier: 'default',
|
|
116
|
+
}, params.runId);
|
|
117
|
+
|
|
118
|
+
return Object.freeze({
|
|
119
|
+
provider: 'cloudflare',
|
|
120
|
+
model,
|
|
121
|
+
response_id: response.id || null,
|
|
122
|
+
status: response?.choices?.[0]?.finish_reason || 'completed',
|
|
123
|
+
output_text: typeof message.content === 'string' ? message.content : '',
|
|
124
|
+
tool_calls: Object.freeze(toolCalls(message)),
|
|
125
|
+
usage_delta: Object.freeze(delta),
|
|
126
|
+
usage_snapshot: usageSnapshot,
|
|
127
|
+
cost: null,
|
|
128
|
+
provider_state: Object.freeze({ messages: [...messages, message] }),
|
|
129
|
+
requested_service_tier: 'default',
|
|
130
|
+
actual_service_tier: 'default',
|
|
131
|
+
raw: response,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function create(params = {}) {
|
|
136
|
+
const prior = Array.isArray(params.providerState?.messages) ? params.providerState.messages : [];
|
|
137
|
+
const messages = [...prior];
|
|
138
|
+
if (clean(params.instructions) && !messages.some((row) => row.role === 'system')) {
|
|
139
|
+
messages.unshift({ role: 'system', content: String(params.instructions) });
|
|
140
|
+
}
|
|
141
|
+
messages.push({ role: 'user', content: typeof params.input === 'string' ? params.input : JSON.stringify(params.input ?? '') });
|
|
142
|
+
return send(messages, params);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function continueWithToolOutputs(params = {}) {
|
|
146
|
+
const prior = Array.isArray(params.providerState?.messages) ? params.providerState.messages : [];
|
|
147
|
+
if (!prior.length) throw new Error('cloudflare_provider_state_required');
|
|
148
|
+
const toolMessages = (params.toolOutputs || []).map((row) => ({
|
|
149
|
+
role: 'tool',
|
|
150
|
+
tool_call_id: row.call_id || row.callId,
|
|
151
|
+
content: typeof row.output === 'string' ? row.output : JSON.stringify(row.output ?? null),
|
|
152
|
+
}));
|
|
153
|
+
if (!toolMessages.length) throw new Error('cloudflare_tool_outputs_required');
|
|
154
|
+
return send([...prior, ...toolMessages], params);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function compact(params = {}) {
|
|
158
|
+
const prior = Array.isArray(params.providerState?.messages) ? params.providerState.messages : [];
|
|
159
|
+
if (!prior.length) throw new Error('cloudflare_provider_state_required');
|
|
160
|
+
const emitFn = params.emit || options.emit;
|
|
161
|
+
const startedAt = Date.now();
|
|
162
|
+
emit(emitFn, 'context.compaction.started', { provider: 'cloudflare', model: params.model }, params.runId);
|
|
163
|
+
const summary = await send([...prior, {
|
|
164
|
+
role: 'user',
|
|
165
|
+
content: 'Create a compact continuation summary of this conversation. Preserve the objective, decisions, changed files, active plan, unresolved failures, tool results that still matter, and constraints. Do not add new work.',
|
|
166
|
+
}], { ...params, tools: [], maxOutputTokens: Math.min(Number(params.maxOutputTokens) || 4096, 4096) });
|
|
167
|
+
const summaryText = String(summary.output_text || '').trim();
|
|
168
|
+
const compactState = Object.freeze({ messages: [
|
|
169
|
+
...(params.instructions ? [{ role: 'system', content: String(params.instructions) }] : []),
|
|
170
|
+
{ role: 'user', content: 'Prior conversation continuation summary:' },
|
|
171
|
+
{ role: 'assistant', content: summaryText },
|
|
172
|
+
] });
|
|
173
|
+
emit(emitFn, 'context.compaction.completed', {
|
|
174
|
+
provider: 'cloudflare', model: params.model, summary_text: summaryText,
|
|
175
|
+
tokens_before: params.tokensBefore ?? null,
|
|
176
|
+
tokens_after: Math.ceil(summaryText.length / 4),
|
|
177
|
+
duration_ms: Date.now() - startedAt,
|
|
178
|
+
}, params.runId);
|
|
179
|
+
return Object.freeze({ provider: 'cloudflare', output: Object.freeze([]), provider_state: compactState, summary_text: summaryText, usage: summary.usage_delta || null, cost: null });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return Object.freeze({ provider: 'cloudflare', create, continueWithToolOutputs, compact });
|
|
183
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { createOpenAIResponsesAdapter } from './openai-responses.js';
|
|
2
|
+
import { createAnthropicMessagesAdapter } from './anthropic-messages.js';
|
|
3
|
+
import { createGeminiGenerateContentAdapter } from './gemini-generate-content.js';
|
|
4
|
+
import { createOllamaChatAdapter } from './ollama-chat.js';
|
|
5
|
+
import { createCloudflareChatAdapter } from './cloudflare-chat.js';
|
|
6
|
+
|
|
7
|
+
export function createProviderAdapter(options = {}) {
|
|
8
|
+
const record = options.modelRecord;
|
|
9
|
+
if (!record?.provider) throw new TypeError('modelRecord.provider is required');
|
|
10
|
+
const provider = String(record.provider).toLowerCase();
|
|
11
|
+
|
|
12
|
+
if (provider === 'openai') {
|
|
13
|
+
return createOpenAIResponsesAdapter({
|
|
14
|
+
apiKey: options.credential?.value,
|
|
15
|
+
modelRecord: record,
|
|
16
|
+
fetchImpl: options.fetchImpl,
|
|
17
|
+
emit: options.emit,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (provider === 'grok' || provider === 'xai') {
|
|
22
|
+
return createOpenAIResponsesAdapter({
|
|
23
|
+
providerId: 'grok',
|
|
24
|
+
baseUrl: 'https://api.x.ai/v1',
|
|
25
|
+
apiKey: options.credential?.value,
|
|
26
|
+
modelRecord: record,
|
|
27
|
+
fetchImpl: options.fetchImpl,
|
|
28
|
+
emit: options.emit,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (provider === 'anthropic') {
|
|
33
|
+
return createAnthropicMessagesAdapter({
|
|
34
|
+
apiKey: options.credential?.value,
|
|
35
|
+
modelRecord: record,
|
|
36
|
+
fetchImpl: options.fetchImpl,
|
|
37
|
+
emit: options.emit,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (provider === 'gemini') {
|
|
42
|
+
return createGeminiGenerateContentAdapter({
|
|
43
|
+
apiKey: options.credential?.value,
|
|
44
|
+
modelRecord: record,
|
|
45
|
+
fetchImpl: options.fetchImpl,
|
|
46
|
+
emit: options.emit,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (provider === 'cloudflare') {
|
|
51
|
+
return createCloudflareChatAdapter({
|
|
52
|
+
credential: options.credential,
|
|
53
|
+
modelRecord: record,
|
|
54
|
+
fetchImpl: options.fetchImpl,
|
|
55
|
+
emit: options.emit,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (provider === 'ollama') {
|
|
60
|
+
return createOllamaChatAdapter({
|
|
61
|
+
endpoint: options.endpoint,
|
|
62
|
+
modelRecord: record,
|
|
63
|
+
fetchImpl: options.fetchImpl,
|
|
64
|
+
emit: options.emit,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
throw new Error(`interactive_provider_adapter_unavailable:${provider}`);
|
|
69
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { createAgentEvent, createUsageSnapshot } from '../telemetry/index.js';
|
|
2
|
+
import { calculateModelCost } from '../models/index.js';
|
|
3
|
+
import { diagnosticFromError } from '../errors/index.js';
|
|
4
|
+
|
|
5
|
+
function clean(value) { return value == null ? '' : String(value).trim(); }
|
|
6
|
+
function integer(value) { const n = Number(value ?? 0); return Number.isFinite(n) && n >= 0 ? Math.floor(n) : 0; }
|
|
7
|
+
function emit(emitFn, type, payload, runId) {
|
|
8
|
+
if (typeof emitFn === 'function') emitFn(createAgentEvent(type, payload, { runId }));
|
|
9
|
+
}
|
|
10
|
+
function signal(ms) {
|
|
11
|
+
return typeof AbortSignal?.timeout === 'function' && Number(ms) > 0 ? AbortSignal.timeout(Number(ms)) : undefined;
|
|
12
|
+
}
|
|
13
|
+
function toolDeclarations(tools = []) {
|
|
14
|
+
return tools.map((tool) => ({
|
|
15
|
+
name: tool.name,
|
|
16
|
+
description: tool.description || '',
|
|
17
|
+
parameters: tool.parameters || { type: 'object', properties: {} },
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
function usageParts(response = {}) {
|
|
21
|
+
const usage = response.usageMetadata || {};
|
|
22
|
+
return {
|
|
23
|
+
input_tokens: integer(usage.promptTokenCount),
|
|
24
|
+
cached_input_tokens: integer(usage.cachedContentTokenCount),
|
|
25
|
+
cache_write_tokens: 0,
|
|
26
|
+
output_tokens: integer(usage.candidatesTokenCount),
|
|
27
|
+
reasoning_tokens: integer(usage.thoughtsTokenCount),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function cumulative(base, delta) {
|
|
31
|
+
return {
|
|
32
|
+
input_tokens: integer(base?.input_tokens) + delta.input_tokens,
|
|
33
|
+
output_tokens: integer(base?.output_tokens) + delta.output_tokens,
|
|
34
|
+
cached_input_tokens: integer(base?.cached_input_tokens) + delta.cached_input_tokens,
|
|
35
|
+
cache_write_tokens: integer(base?.cache_write_tokens),
|
|
36
|
+
reasoning_tokens: integer(base?.reasoning_tokens) + delta.reasoning_tokens,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function candidateContent(response = {}) {
|
|
40
|
+
return response?.candidates?.[0]?.content || { role: 'model', parts: [] };
|
|
41
|
+
}
|
|
42
|
+
function outputText(content = {}) {
|
|
43
|
+
return (content.parts || []).filter((part) => typeof part?.text === 'string').map((part) => part.text).join('');
|
|
44
|
+
}
|
|
45
|
+
function toolCalls(content = {}) {
|
|
46
|
+
const calls = [];
|
|
47
|
+
let index = 0;
|
|
48
|
+
for (const part of content.parts || []) {
|
|
49
|
+
if (!part?.functionCall?.name) continue;
|
|
50
|
+
index += 1;
|
|
51
|
+
calls.push({
|
|
52
|
+
call_id: `gcall_${index}_${clean(part.functionCall.name).replace(/[^A-Za-z0-9_-]/g, '_')}`,
|
|
53
|
+
name: part.functionCall.name,
|
|
54
|
+
arguments: JSON.stringify(part.functionCall.args || {}),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return calls;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function createGeminiGenerateContentAdapter(options = {}) {
|
|
61
|
+
const apiKey = clean(options.apiKey || process.env.GEMINI_API_KEY);
|
|
62
|
+
const fetchImpl = options.fetchImpl || fetch;
|
|
63
|
+
const baseUrl = clean(options.baseUrl || 'https://generativelanguage.googleapis.com/v1beta').replace(/\/$/, '');
|
|
64
|
+
|
|
65
|
+
async function request(model, body, params = {}) {
|
|
66
|
+
if (!apiKey) throw new Error('gemini_api_key_required');
|
|
67
|
+
const url = `${baseUrl}/models/${encodeURIComponent(model)}:generateContent?key=${encodeURIComponent(apiKey)}`;
|
|
68
|
+
const response = await fetchImpl(url, {
|
|
69
|
+
method: 'POST',
|
|
70
|
+
headers: { 'content-type': 'application/json' },
|
|
71
|
+
body: JSON.stringify(body),
|
|
72
|
+
signal: signal(params.timeoutMs || options.timeoutMs),
|
|
73
|
+
});
|
|
74
|
+
const data = await response.json().catch(() => ({}));
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
const error = new Error(data?.error?.message || data?.message || `Gemini HTTP ${response.status}`);
|
|
77
|
+
error.status = response.status;
|
|
78
|
+
error.provider = 'gemini';
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
return data;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function send(contents, params = {}) {
|
|
85
|
+
const record = params.modelRecord || options.modelRecord;
|
|
86
|
+
if (!record || record.provider !== 'gemini') throw new Error('gemini_model_record_required');
|
|
87
|
+
const emitFn = params.emit || options.emit;
|
|
88
|
+
const runId = params.runId;
|
|
89
|
+
const model = record.provider_model_id;
|
|
90
|
+
const declarations = toolDeclarations(params.tools || []);
|
|
91
|
+
const body = {
|
|
92
|
+
contents,
|
|
93
|
+
...(clean(params.instructions) ? { systemInstruction: { parts: [{ text: String(params.instructions) }] } } : {}),
|
|
94
|
+
...(declarations.length ? { tools: [{ functionDeclarations: declarations }] } : {}),
|
|
95
|
+
generationConfig: {
|
|
96
|
+
...(Number(params.maxOutputTokens) > 0 ? { maxOutputTokens: Number(params.maxOutputTokens) } : {}),
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
emit(emitFn, 'model.started', {
|
|
101
|
+
provider: 'gemini',
|
|
102
|
+
model,
|
|
103
|
+
reasoning_effort: params.reasoningEffort || 'auto',
|
|
104
|
+
requested_service_tier: 'default',
|
|
105
|
+
}, runId);
|
|
106
|
+
|
|
107
|
+
let response;
|
|
108
|
+
try {
|
|
109
|
+
response = await request(model, body, params);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
const diagnostic = diagnosticFromError(error, { source: 'gemini', kind: 'provider_error' });
|
|
112
|
+
emit(emitFn, 'error.observed', diagnostic, runId);
|
|
113
|
+
emit(emitFn, 'run.failed', { stage: 'model', provider: 'gemini', model, error: diagnostic }, runId);
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const content = candidateContent(response);
|
|
118
|
+
const calls = toolCalls(content);
|
|
119
|
+
const delta = usageParts(response);
|
|
120
|
+
const usageSnapshot = createUsageSnapshot({
|
|
121
|
+
current_context: { input_tokens: delta.input_tokens, window_tokens: Number(record.context_window) || 0 },
|
|
122
|
+
cumulative: cumulative(params.cumulativeUsage, delta),
|
|
123
|
+
estimate_kind: 'provider',
|
|
124
|
+
});
|
|
125
|
+
const cost = record.pricing ? calculateModelCost(record, delta, { serviceTier: 'default' }) : null;
|
|
126
|
+
emit(emitFn, 'usage.snapshot', usageSnapshot, runId);
|
|
127
|
+
if (cost) emit(emitFn, 'cost.snapshot', cost, runId);
|
|
128
|
+
emit(emitFn, 'model.completed', {
|
|
129
|
+
provider: 'gemini',
|
|
130
|
+
model,
|
|
131
|
+
response_id: response.responseId || null,
|
|
132
|
+
status: response?.candidates?.[0]?.finishReason || 'completed',
|
|
133
|
+
requested_service_tier: 'default',
|
|
134
|
+
actual_service_tier: 'default',
|
|
135
|
+
}, runId);
|
|
136
|
+
|
|
137
|
+
return Object.freeze({
|
|
138
|
+
provider: 'gemini',
|
|
139
|
+
model,
|
|
140
|
+
response_id: response.responseId || null,
|
|
141
|
+
status: response?.candidates?.[0]?.finishReason || 'completed',
|
|
142
|
+
output_text: outputText(content),
|
|
143
|
+
tool_calls: Object.freeze(calls),
|
|
144
|
+
usage_delta: Object.freeze(delta),
|
|
145
|
+
usage_snapshot: usageSnapshot,
|
|
146
|
+
cost,
|
|
147
|
+
provider_state: Object.freeze({
|
|
148
|
+
contents: [...contents, content],
|
|
149
|
+
pending_tools: calls.map((call) => ({ call_id: call.call_id, name: call.name })),
|
|
150
|
+
}),
|
|
151
|
+
requested_service_tier: 'default',
|
|
152
|
+
actual_service_tier: 'default',
|
|
153
|
+
raw: response,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function create(params = {}) {
|
|
158
|
+
const prior = Array.isArray(params.providerState?.contents) ? params.providerState.contents : [];
|
|
159
|
+
const input = typeof params.input === 'string' ? params.input : JSON.stringify(params.input ?? '');
|
|
160
|
+
return send([...prior, { role: 'user', parts: [{ text: input }] }], params);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function continueWithToolOutputs(params = {}) {
|
|
164
|
+
const prior = Array.isArray(params.providerState?.contents) ? params.providerState.contents : [];
|
|
165
|
+
const pending = Array.isArray(params.providerState?.pending_tools) ? params.providerState.pending_tools : [];
|
|
166
|
+
if (!prior.length) throw new Error('gemini_provider_state_required');
|
|
167
|
+
const byId = new Map(pending.map((row) => [row.call_id, row.name]));
|
|
168
|
+
const parts = (params.toolOutputs || []).map((row) => {
|
|
169
|
+
const callId = row.call_id || row.callId;
|
|
170
|
+
const name = byId.get(callId);
|
|
171
|
+
if (!name) throw new Error(`gemini_tool_call_unknown:${callId}`);
|
|
172
|
+
let responseValue = row.output;
|
|
173
|
+
if (typeof responseValue === 'string') {
|
|
174
|
+
try { responseValue = JSON.parse(responseValue); }
|
|
175
|
+
catch { responseValue = { output: responseValue }; }
|
|
176
|
+
}
|
|
177
|
+
return { functionResponse: { name, response: responseValue ?? {} } };
|
|
178
|
+
});
|
|
179
|
+
if (!parts.length) throw new Error('gemini_tool_outputs_required');
|
|
180
|
+
return send([...prior, { role: 'user', parts }], params);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function compact(params = {}) {
|
|
184
|
+
const prior = Array.isArray(params.providerState?.contents) ? params.providerState.contents : [];
|
|
185
|
+
if (!prior.length) throw new Error('gemini_provider_state_required');
|
|
186
|
+
const emitFn = params.emit || options.emit;
|
|
187
|
+
const startedAt = Date.now();
|
|
188
|
+
emit(emitFn, 'context.compaction.started', { provider: 'gemini', model: params.model }, params.runId);
|
|
189
|
+
const summary = await send([...prior, {
|
|
190
|
+
role: 'user',
|
|
191
|
+
parts: [{ text: 'Create a compact continuation summary of this conversation. Preserve the objective, decisions, changed files, active plan, unresolved failures, tool results that still matter, and constraints. Do not add new work.' }],
|
|
192
|
+
}], { ...params, tools: [], maxOutputTokens: Math.min(Number(params.maxOutputTokens) || 4096, 4096) });
|
|
193
|
+
const summaryText = String(summary.output_text || '').trim();
|
|
194
|
+
const compactState = Object.freeze({ contents: [
|
|
195
|
+
{ role: 'user', parts: [{ text: 'Prior conversation continuation summary:' }] },
|
|
196
|
+
{ role: 'model', parts: [{ text: summaryText }] },
|
|
197
|
+
], pending_tools: [] });
|
|
198
|
+
emit(emitFn, 'context.compaction.completed', {
|
|
199
|
+
provider: 'gemini', model: params.model, summary_text: summaryText,
|
|
200
|
+
tokens_before: params.tokensBefore ?? null,
|
|
201
|
+
tokens_after: Math.ceil(summaryText.length / 4),
|
|
202
|
+
duration_ms: Date.now() - startedAt,
|
|
203
|
+
}, params.runId);
|
|
204
|
+
return Object.freeze({ provider: 'gemini', output: Object.freeze([]), provider_state: compactState, summary_text: summaryText, usage: summary.usage_delta || null });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return Object.freeze({ provider: 'gemini', create, continueWithToolOutputs, compact });
|
|
208
|
+
}
|
package/src/providers/index.js
CHANGED
|
@@ -3,3 +3,8 @@ export {
|
|
|
3
3
|
extractOpenAIOutputText,
|
|
4
4
|
extractOpenAIFunctionCalls,
|
|
5
5
|
} from './openai-responses.js';
|
|
6
|
+
export { createAnthropicMessagesAdapter } from './anthropic-messages.js';
|
|
7
|
+
export { createGeminiGenerateContentAdapter } from './gemini-generate-content.js';
|
|
8
|
+
export { createOllamaChatAdapter } from './ollama-chat.js';
|
|
9
|
+
export { createCloudflareChatAdapter } from './cloudflare-chat.js';
|
|
10
|
+
export { createProviderAdapter } from './factory.js';
|