@strav/brain 1.0.0-alpha.8 → 1.0.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/package.json +23 -7
- package/src/agent.ts +97 -0
- package/src/agent_generate_result.ts +32 -0
- package/src/agent_result.ts +39 -0
- package/src/agent_runner.ts +265 -0
- package/src/agent_stream_event.ts +100 -0
- package/src/brain_config.ts +218 -1
- package/src/brain_driver.ts +247 -0
- package/src/brain_error.ts +86 -10
- package/src/brain_manager.ts +419 -5
- package/src/brain_provider.ts +89 -10
- package/src/define_tool.ts +42 -0
- package/src/drivers/anthropic/anthropic_brain_driver.ts +641 -0
- package/src/drivers/anthropic/anthropic_helpers.ts +65 -0
- package/src/drivers/anthropic/anthropic_message_builder.ts +258 -0
- package/src/drivers/anthropic/anthropic_response_mapper.ts +123 -0
- package/src/drivers/anthropic/anthropic_tool_loop.ts +246 -0
- package/src/drivers/anthropic/index.ts +1 -0
- package/src/drivers/deepseek/deepseek_brain_driver.ts +117 -0
- package/src/drivers/deepseek/index.ts +1 -0
- package/src/drivers/gemini/gemini_brain_driver.ts +1064 -0
- package/src/drivers/gemini/index.ts +1 -0
- package/src/drivers/minimax/index.ts +1 -0
- package/src/drivers/minimax/minimax_brain_driver.ts +84 -0
- package/src/drivers/ollama/index.ts +1 -0
- package/src/drivers/ollama/ollama_brain_driver.ts +86 -0
- package/src/drivers/openai/index.ts +1 -0
- package/src/drivers/openai/openai_brain_driver.ts +796 -0
- package/src/drivers/openai/openai_helpers.ts +58 -0
- package/src/drivers/openai/openai_message_builder.ts +187 -0
- package/src/drivers/openai/openai_response_mapper.ts +70 -0
- package/src/drivers/openai/openai_tool_dispatch.ts +127 -0
- package/src/drivers/openai/openai_tool_loop.ts +191 -0
- package/src/drivers/openai_compat/index.ts +1 -0
- package/src/drivers/openai_compat/openai_compat_brain_driver.ts +616 -0
- package/src/drivers/openai_responses/index.ts +1 -0
- package/src/drivers/openai_responses/openai_responses_brain_driver.ts +1015 -0
- package/src/drivers/openrouter/index.ts +1 -0
- package/src/drivers/openrouter/openrouter_brain_driver.ts +137 -0
- package/src/drivers/qwen/index.ts +1 -0
- package/src/drivers/qwen/qwen_brain_driver.ts +103 -0
- package/src/index.ts +86 -8
- package/src/mcp/client.ts +243 -0
- package/src/mcp/index.ts +23 -0
- package/src/mcp/oauth.ts +227 -0
- package/src/mcp/pool.ts +106 -0
- package/src/mcp/resolve_mcp_tools.ts +108 -0
- package/src/mcp_server.ts +63 -0
- package/src/output_schema.ts +72 -0
- package/src/persistence/brain_message.ts +34 -0
- package/src/persistence/brain_message_repository.ts +98 -0
- package/src/persistence/brain_store.ts +166 -0
- package/src/persistence/brain_suspended_run.ts +30 -0
- package/src/persistence/brain_suspended_run_repository.ts +59 -0
- package/src/persistence/brain_thread.ts +30 -0
- package/src/persistence/brain_thread_repository.ts +56 -0
- package/src/persistence/database_brain_store.ts +190 -0
- package/src/persistence/index.ts +48 -0
- package/src/persistence/schemas/brain_message_schema.ts +61 -0
- package/src/persistence/schemas/brain_suspended_run_schema.ts +58 -0
- package/src/persistence/schemas/brain_thread_schema.ts +50 -0
- package/src/persistence/schemas/index.ts +3 -0
- package/src/suspended_run.ts +153 -0
- package/src/thread.ts +40 -1
- package/src/tool.ts +42 -0
- package/src/tool_execution_error.ts +26 -0
- package/src/tool_runner.ts +81 -0
- package/src/translate/index.ts +19 -0
- package/src/translate/translate_cache.ts +78 -0
- package/src/translate/translate_provider.ts +46 -0
- package/src/translate/translator.ts +271 -0
- package/src/types.ts +431 -1
- package/src/zod/index.ts +121 -0
- package/src/provider.ts +0 -48
- package/src/providers/anthropic_provider.ts +0 -227
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `AnthropicProvider` — implementation of `Provider` backed by the
|
|
3
|
-
* official `@anthropic-ai/sdk`.
|
|
4
|
-
*
|
|
5
|
-
* Responsibilities:
|
|
6
|
-
* 1. Hold a singleton `Anthropic` client instance for the
|
|
7
|
-
* configured API key + base URL.
|
|
8
|
-
* 2. Translate the framework's `ChatOptions` / `Message` shapes
|
|
9
|
-
* into Anthropic's `MessageCreateParams` (system as `TextBlock[]`
|
|
10
|
-
* with `cache_control` when requested; messages with per-block
|
|
11
|
-
* cache flags translated likewise; `thinking` mapped to
|
|
12
|
-
* `ThinkingConfigParam`; `effort` placed under `output_config`).
|
|
13
|
-
* 3. Translate the response back to `ChatResult` — flatten the
|
|
14
|
-
* content blocks into a single `text` string, surface usage with
|
|
15
|
-
* cache-hit counters, and pass the raw `Message` through on `.raw`.
|
|
16
|
-
* 4. Stream via `client.messages.stream()` and yield the framework
|
|
17
|
-
* `StreamEvent` union — `text` deltas plus a terminal `stop`
|
|
18
|
-
* event with usage + stop reason.
|
|
19
|
-
*
|
|
20
|
-
* Errors from the SDK propagate; apps that want provider-specific
|
|
21
|
-
* recovery can `instanceof Anthropic.RateLimitError` etc. The brain
|
|
22
|
-
* facade wraps the call site in `BrainError` only for invariants the
|
|
23
|
-
* facade owns (e.g. "no provider configured").
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
import Anthropic from '@anthropic-ai/sdk'
|
|
27
|
-
import type { AnthropicProviderConfig } from '../brain_config.ts'
|
|
28
|
-
import { DEFAULT_MODEL } from '../brain_config.ts'
|
|
29
|
-
import type { Provider } from '../provider.ts'
|
|
30
|
-
import type {
|
|
31
|
-
ChatOptions,
|
|
32
|
-
ChatResult,
|
|
33
|
-
ChatUsage,
|
|
34
|
-
Message,
|
|
35
|
-
StreamEvent,
|
|
36
|
-
SystemPrompt,
|
|
37
|
-
} from '../types.ts'
|
|
38
|
-
|
|
39
|
-
const EPHEMERAL_CACHE = { type: 'ephemeral' } as const
|
|
40
|
-
|
|
41
|
-
export class AnthropicProvider implements Provider {
|
|
42
|
-
readonly name: string
|
|
43
|
-
private readonly client: Anthropic
|
|
44
|
-
private readonly defaultModel: string
|
|
45
|
-
private readonly defaultMaxTokens: number
|
|
46
|
-
private readonly betas: readonly string[]
|
|
47
|
-
|
|
48
|
-
constructor(
|
|
49
|
-
name: string,
|
|
50
|
-
config: AnthropicProviderConfig,
|
|
51
|
-
options: { client?: Anthropic } = {},
|
|
52
|
-
) {
|
|
53
|
-
this.name = name
|
|
54
|
-
this.defaultModel = config.defaultModel ?? DEFAULT_MODEL
|
|
55
|
-
this.defaultMaxTokens = config.defaultMaxTokens ?? 4096
|
|
56
|
-
this.betas = config.betas ?? []
|
|
57
|
-
// `client` injection point — tests pass a stub; apps that want a
|
|
58
|
-
// pre-configured SDK instance (custom retry, fetch transport, etc.)
|
|
59
|
-
// build their own and hand it over here.
|
|
60
|
-
this.client =
|
|
61
|
-
options.client ??
|
|
62
|
-
new Anthropic({
|
|
63
|
-
apiKey: config.apiKey,
|
|
64
|
-
...(config.baseUrl !== undefined ? { baseURL: config.baseUrl } : {}),
|
|
65
|
-
})
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async chat(messages: readonly Message[], options: ChatOptions = {}): Promise<ChatResult> {
|
|
69
|
-
const params = this.buildParams(messages, options)
|
|
70
|
-
const response = await this.client.messages.create(params)
|
|
71
|
-
return this.toChatResult(response)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async *stream(
|
|
75
|
-
messages: readonly Message[],
|
|
76
|
-
options: ChatOptions = {},
|
|
77
|
-
): AsyncIterable<StreamEvent> {
|
|
78
|
-
const params = this.buildParams(messages, options)
|
|
79
|
-
const stream = this.client.messages.stream(params)
|
|
80
|
-
for await (const event of stream) {
|
|
81
|
-
if (
|
|
82
|
-
event.type === 'content_block_delta' &&
|
|
83
|
-
event.delta.type === 'text_delta'
|
|
84
|
-
) {
|
|
85
|
-
yield { type: 'text', delta: event.delta.text }
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
const final = await stream.finalMessage()
|
|
89
|
-
yield {
|
|
90
|
-
type: 'stop',
|
|
91
|
-
stopReason: final.stop_reason,
|
|
92
|
-
usage: toUsage(final.usage),
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async countTokens(
|
|
97
|
-
messages: readonly Message[],
|
|
98
|
-
options: ChatOptions = {},
|
|
99
|
-
): Promise<number> {
|
|
100
|
-
const base = this.buildParams(messages, options)
|
|
101
|
-
// count_tokens only accepts a subset of MessageCreateParams; build
|
|
102
|
-
// a focused payload that matches what apps actually need to budget.
|
|
103
|
-
const result = await this.client.messages.countTokens({
|
|
104
|
-
model: base.model,
|
|
105
|
-
messages: base.messages,
|
|
106
|
-
...(base.system !== undefined ? { system: base.system } : {}),
|
|
107
|
-
...(base.thinking !== undefined ? { thinking: base.thinking } : {}),
|
|
108
|
-
})
|
|
109
|
-
return result.input_tokens
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// ─── Param translation ──────────────────────────────────────────────────
|
|
113
|
-
|
|
114
|
-
private buildParams(
|
|
115
|
-
messages: readonly Message[],
|
|
116
|
-
options: ChatOptions,
|
|
117
|
-
): Anthropic.MessageCreateParamsNonStreaming {
|
|
118
|
-
const model = options.model ?? this.defaultModel
|
|
119
|
-
const params: Anthropic.MessageCreateParamsNonStreaming = {
|
|
120
|
-
model,
|
|
121
|
-
max_tokens: options.maxTokens ?? this.defaultMaxTokens,
|
|
122
|
-
messages: messages.map(toMessageParam),
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const system = toSystemParam(options.system)
|
|
126
|
-
if (system !== undefined) params.system = system
|
|
127
|
-
|
|
128
|
-
if (options.thinking === 'adaptive') {
|
|
129
|
-
params.thinking = { type: 'adaptive' }
|
|
130
|
-
} else if (options.thinking === 'disabled') {
|
|
131
|
-
params.thinking = { type: 'disabled' }
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (options.effort !== undefined) {
|
|
135
|
-
params.output_config = { effort: options.effort }
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (options.cache === true) {
|
|
139
|
-
// Top-level auto-cache the last cacheable block. Maps to the
|
|
140
|
-
// SDK's `cache_control` shorthand on the request body.
|
|
141
|
-
;(params as { cache_control?: { type: 'ephemeral' } }).cache_control = EPHEMERAL_CACHE
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const betas = mergeBetas(this.betas, options.betas)
|
|
145
|
-
if (betas.length > 0) {
|
|
146
|
-
;(params as { betas?: readonly string[] }).betas = betas
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return params
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
private toChatResult(message: Anthropic.Message): ChatResult<Anthropic.Message> {
|
|
153
|
-
const text = message.content
|
|
154
|
-
.filter((b): b is Anthropic.TextBlock => b.type === 'text')
|
|
155
|
-
.map((b) => b.text)
|
|
156
|
-
.join('')
|
|
157
|
-
return {
|
|
158
|
-
text,
|
|
159
|
-
model: message.model,
|
|
160
|
-
stopReason: message.stop_reason,
|
|
161
|
-
usage: toUsage(message.usage),
|
|
162
|
-
raw: message,
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// ─── Shape converters ─────────────────────────────────────────────────────
|
|
168
|
-
|
|
169
|
-
function toUsage(u: Anthropic.Usage): ChatUsage {
|
|
170
|
-
return {
|
|
171
|
-
inputTokens: u.input_tokens,
|
|
172
|
-
outputTokens: u.output_tokens,
|
|
173
|
-
cacheReadTokens: u.cache_read_input_tokens ?? 0,
|
|
174
|
-
cacheCreationTokens: u.cache_creation_input_tokens ?? 0,
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function toMessageParam(message: Message): Anthropic.MessageParam {
|
|
179
|
-
if (typeof message.content === 'string') {
|
|
180
|
-
return { role: message.role, content: message.content }
|
|
181
|
-
}
|
|
182
|
-
return {
|
|
183
|
-
role: message.role,
|
|
184
|
-
content: message.content.map((block) => {
|
|
185
|
-
const param: Anthropic.TextBlockParam = { type: 'text', text: block.text }
|
|
186
|
-
if (block.cache) param.cache_control = EPHEMERAL_CACHE
|
|
187
|
-
return param
|
|
188
|
-
}),
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function toSystemParam(
|
|
193
|
-
system: SystemPrompt | undefined,
|
|
194
|
-
): string | Anthropic.TextBlockParam[] | undefined {
|
|
195
|
-
if (system === undefined) return undefined
|
|
196
|
-
if (typeof system === 'string') return system
|
|
197
|
-
if (Array.isArray(system)) {
|
|
198
|
-
return system.map((block) => {
|
|
199
|
-
const param: Anthropic.TextBlockParam = { type: 'text', text: block.text }
|
|
200
|
-
if (block.cache) param.cache_control = EPHEMERAL_CACHE
|
|
201
|
-
return param
|
|
202
|
-
})
|
|
203
|
-
}
|
|
204
|
-
const param: Anthropic.TextBlockParam = { type: 'text', text: system.text }
|
|
205
|
-
if (system.cache) param.cache_control = EPHEMERAL_CACHE
|
|
206
|
-
return [param]
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function mergeBetas(
|
|
210
|
-
providerBetas: readonly string[],
|
|
211
|
-
callBetas: readonly string[] | undefined,
|
|
212
|
-
): readonly string[] {
|
|
213
|
-
if (!callBetas || callBetas.length === 0) return providerBetas
|
|
214
|
-
const seen = new Set<string>()
|
|
215
|
-
const out: string[] = []
|
|
216
|
-
for (const b of providerBetas) {
|
|
217
|
-
if (seen.has(b)) continue
|
|
218
|
-
seen.add(b)
|
|
219
|
-
out.push(b)
|
|
220
|
-
}
|
|
221
|
-
for (const b of callBetas) {
|
|
222
|
-
if (seen.has(b)) continue
|
|
223
|
-
seen.add(b)
|
|
224
|
-
out.push(b)
|
|
225
|
-
}
|
|
226
|
-
return out
|
|
227
|
-
}
|