hoomanjs 1.0.0
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/.github/screenshot.png +0 -0
- package/.github/workflows/build-publish.yml +49 -0
- package/LICENSE +21 -0
- package/README.md +399 -0
- package/docker-compose.yml +13 -0
- package/package.json +78 -0
- package/src/acp/acp-agent.ts +803 -0
- package/src/acp/approvals.ts +147 -0
- package/src/acp/index.ts +1 -0
- package/src/acp/meta/system-prompt.ts +44 -0
- package/src/acp/meta/user-id.ts +44 -0
- package/src/acp/prompt-invoke.ts +149 -0
- package/src/acp/sessions/config-options.ts +56 -0
- package/src/acp/sessions/replay.ts +131 -0
- package/src/acp/sessions/store.ts +158 -0
- package/src/acp/sessions/title.ts +22 -0
- package/src/acp/utils/paths.ts +5 -0
- package/src/acp/utils/tool-kind.ts +38 -0
- package/src/acp/utils/tool-locations.ts +46 -0
- package/src/acp/utils/tool-result-content.ts +27 -0
- package/src/chat/app.tsx +428 -0
- package/src/chat/approvals.ts +96 -0
- package/src/chat/components/ApprovalPrompt.tsx +25 -0
- package/src/chat/components/ChatMessage.tsx +47 -0
- package/src/chat/components/Composer.tsx +39 -0
- package/src/chat/components/EmptyChatBanner.tsx +26 -0
- package/src/chat/components/ReasoningStrip.tsx +30 -0
- package/src/chat/components/Spinner.tsx +34 -0
- package/src/chat/components/StatusBar.tsx +65 -0
- package/src/chat/components/ThinkingStatus.tsx +128 -0
- package/src/chat/components/ToolEvent.tsx +34 -0
- package/src/chat/components/Transcript.tsx +34 -0
- package/src/chat/components/ascii-logo.ts +11 -0
- package/src/chat/components/shared.ts +70 -0
- package/src/chat/index.tsx +42 -0
- package/src/chat/types.ts +21 -0
- package/src/cli.ts +146 -0
- package/src/configure/app.tsx +911 -0
- package/src/configure/components/BusyScreen.tsx +22 -0
- package/src/configure/components/HomeScreen.tsx +43 -0
- package/src/configure/components/MenuScreen.tsx +44 -0
- package/src/configure/components/PromptForm.tsx +40 -0
- package/src/configure/components/SelectMenuItem.tsx +30 -0
- package/src/configure/index.tsx +43 -0
- package/src/configure/open-in-editor.ts +133 -0
- package/src/configure/types.ts +45 -0
- package/src/configure/utils.ts +113 -0
- package/src/core/agent/index.ts +76 -0
- package/src/core/config.ts +157 -0
- package/src/core/index.ts +54 -0
- package/src/core/mcp/config.ts +80 -0
- package/src/core/mcp/index.ts +13 -0
- package/src/core/mcp/manager.ts +109 -0
- package/src/core/mcp/prefixed-mcp-tool.ts +45 -0
- package/src/core/mcp/tools.ts +92 -0
- package/src/core/mcp/types.ts +37 -0
- package/src/core/memory/index.ts +17 -0
- package/src/core/memory/ltm/embed.ts +67 -0
- package/src/core/memory/ltm/index.ts +18 -0
- package/src/core/memory/ltm/store.ts +376 -0
- package/src/core/memory/ltm/tools.ts +146 -0
- package/src/core/memory/ltm/types.ts +111 -0
- package/src/core/memory/ltm/utils.ts +218 -0
- package/src/core/memory/stm/index.ts +17 -0
- package/src/core/models/anthropic.ts +53 -0
- package/src/core/models/bedrock.ts +54 -0
- package/src/core/models/google.ts +51 -0
- package/src/core/models/index.ts +16 -0
- package/src/core/models/ollama/index.ts +13 -0
- package/src/core/models/ollama/strands-ollama.ts +439 -0
- package/src/core/models/openai.ts +12 -0
- package/src/core/prompts/index.ts +23 -0
- package/src/core/prompts/skills.ts +66 -0
- package/src/core/prompts/static/fetch.md +33 -0
- package/src/core/prompts/static/filesystem.md +38 -0
- package/src/core/prompts/static/identity.md +22 -0
- package/src/core/prompts/static/ltm.md +39 -0
- package/src/core/prompts/static/memory.md +39 -0
- package/src/core/prompts/static/shell.md +34 -0
- package/src/core/prompts/static/skills.md +19 -0
- package/src/core/prompts/static/thinking.md +27 -0
- package/src/core/prompts/system.ts +109 -0
- package/src/core/skills/index.ts +2 -0
- package/src/core/skills/registry.ts +239 -0
- package/src/core/skills/tools.ts +80 -0
- package/src/core/toolkit.ts +13 -0
- package/src/core/tools/fetch.ts +288 -0
- package/src/core/tools/filesystem.ts +747 -0
- package/src/core/tools/index.ts +5 -0
- package/src/core/tools/shell.ts +426 -0
- package/src/core/tools/thinking.ts +184 -0
- package/src/core/tools/time.ts +121 -0
- package/src/core/utils/cwd-context.ts +11 -0
- package/src/core/utils/paths.ts +28 -0
- package/src/exec/approvals.ts +85 -0
- package/tsconfig.json +30 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import type { Where } from "chromadb";
|
|
2
|
+
import type {
|
|
3
|
+
LongTermMemoryScope,
|
|
4
|
+
Memory,
|
|
5
|
+
MemorySource,
|
|
6
|
+
MemoryStatus,
|
|
7
|
+
MemoryType,
|
|
8
|
+
} from "./types.ts";
|
|
9
|
+
|
|
10
|
+
export const DEFAULT_HALF_LIFE_MS = 1000 * 60 * 60 * 24 * 30;
|
|
11
|
+
export const DEFAULT_REINFORCEMENT_STEP = 0.1;
|
|
12
|
+
export const DEFAULT_DEDUPE_THRESHOLD = 0.92;
|
|
13
|
+
const DEFAULT_SEARCH_LIMIT = 5;
|
|
14
|
+
const MAX_SEARCH_LIMIT = 20;
|
|
15
|
+
|
|
16
|
+
export type ChromaMemoryMetadata = {
|
|
17
|
+
userId: string;
|
|
18
|
+
type: MemoryType;
|
|
19
|
+
status: MemoryStatus;
|
|
20
|
+
importance: number;
|
|
21
|
+
strength: number;
|
|
22
|
+
accessCount: number;
|
|
23
|
+
confidence: number | null;
|
|
24
|
+
createdAt: number;
|
|
25
|
+
updatedAt: number | null;
|
|
26
|
+
lastAccessedAt: number | null;
|
|
27
|
+
version: number;
|
|
28
|
+
source: MemorySource;
|
|
29
|
+
/** Omitted when empty: Chroma rejects `[]` for list metadata values. */
|
|
30
|
+
tags?: string[];
|
|
31
|
+
entities?: string[];
|
|
32
|
+
relatedTo?: string[];
|
|
33
|
+
supersededBy: string | null;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export function clampSearchLimit(limit?: number): number {
|
|
37
|
+
const value = Math.trunc(limit ?? DEFAULT_SEARCH_LIMIT);
|
|
38
|
+
return Math.min(Math.max(value, 1), MAX_SEARCH_LIMIT);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function clampUnitInterval(value: number, fallback: number): number {
|
|
42
|
+
if (!Number.isFinite(value)) {
|
|
43
|
+
return fallback;
|
|
44
|
+
}
|
|
45
|
+
return Math.min(1, Math.max(0, value));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function asStringArray(value: unknown): string[] | undefined {
|
|
49
|
+
if (Array.isArray(value)) {
|
|
50
|
+
return value.filter((item): item is string => typeof item === "string");
|
|
51
|
+
}
|
|
52
|
+
if (typeof value === "string" && value.length > 0) {
|
|
53
|
+
return [value];
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function normalizeText(text: string): string {
|
|
59
|
+
return text
|
|
60
|
+
.toLowerCase()
|
|
61
|
+
.replace(/[^\p{L}\p{N}\s]/gu, " ")
|
|
62
|
+
.replace(/\s+/g, " ")
|
|
63
|
+
.trim();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function similarity(left: string, right: string): number {
|
|
67
|
+
const a = normalizeText(left);
|
|
68
|
+
const b = normalizeText(right);
|
|
69
|
+
if (!a || !b) {
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
if (a === b) {
|
|
73
|
+
return 1;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const shorter = a.length <= b.length ? a : b;
|
|
77
|
+
const longer = a.length > b.length ? a : b;
|
|
78
|
+
const containment = longer.includes(shorter)
|
|
79
|
+
? shorter.length / longer.length
|
|
80
|
+
: 0;
|
|
81
|
+
|
|
82
|
+
const tokensA = new Set(a.split(" "));
|
|
83
|
+
const tokensB = new Set(b.split(" "));
|
|
84
|
+
let intersection = 0;
|
|
85
|
+
for (const token of tokensA) {
|
|
86
|
+
if (tokensB.has(token)) {
|
|
87
|
+
intersection += 1;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const union = new Set([...tokensA, ...tokensB]).size;
|
|
91
|
+
const jaccard = union === 0 ? 0 : intersection / union;
|
|
92
|
+
|
|
93
|
+
return Math.max(containment, jaccard);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function getEffectiveStrength(
|
|
97
|
+
metadata: Pick<
|
|
98
|
+
Memory["metadata"],
|
|
99
|
+
"createdAt" | "lastAccessedAt" | "strength"
|
|
100
|
+
>,
|
|
101
|
+
halfLifeMs: number,
|
|
102
|
+
): number {
|
|
103
|
+
const reference = metadata.lastAccessedAt ?? metadata.createdAt;
|
|
104
|
+
const age = Math.max(0, Date.now() - reference);
|
|
105
|
+
return metadata.strength * Math.exp(-age / halfLifeMs);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function toChromaMetadata(memory: Memory): ChromaMemoryMetadata {
|
|
109
|
+
const tags = (memory.metadata.tags ?? []).filter((t) => t.length > 0);
|
|
110
|
+
const entities = (memory.metadata.entities ?? []).filter((t) => t.length > 0);
|
|
111
|
+
const relatedTo = (memory.metadata.relatedTo ?? []).filter(
|
|
112
|
+
(t) => t.length > 0,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const meta: ChromaMemoryMetadata = {
|
|
116
|
+
userId: memory.userId,
|
|
117
|
+
type: memory.type,
|
|
118
|
+
status: memory.status,
|
|
119
|
+
importance: memory.metadata.importance,
|
|
120
|
+
strength: memory.metadata.strength,
|
|
121
|
+
accessCount: memory.metadata.accessCount,
|
|
122
|
+
confidence: memory.metadata.confidence ?? null,
|
|
123
|
+
createdAt: memory.metadata.createdAt,
|
|
124
|
+
updatedAt: memory.metadata.updatedAt ?? null,
|
|
125
|
+
lastAccessedAt: memory.metadata.lastAccessedAt ?? null,
|
|
126
|
+
version: memory.metadata.version,
|
|
127
|
+
source: memory.metadata.source,
|
|
128
|
+
supersededBy: memory.metadata.supersededBy ?? null,
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
if (tags.length > 0) {
|
|
132
|
+
meta.tags = tags;
|
|
133
|
+
}
|
|
134
|
+
if (entities.length > 0) {
|
|
135
|
+
meta.entities = entities;
|
|
136
|
+
}
|
|
137
|
+
if (relatedTo.length > 0) {
|
|
138
|
+
meta.relatedTo = relatedTo;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return meta;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function toMemory(
|
|
145
|
+
id: string,
|
|
146
|
+
content: string,
|
|
147
|
+
metadata: ChromaMemoryMetadata | null | undefined,
|
|
148
|
+
): Memory {
|
|
149
|
+
const createdAt = Number(metadata?.createdAt ?? Date.now());
|
|
150
|
+
return {
|
|
151
|
+
id,
|
|
152
|
+
userId: metadata?.userId ?? "",
|
|
153
|
+
type: (metadata?.type ?? "semantic") as MemoryType,
|
|
154
|
+
status: (metadata?.status ?? "active") as MemoryStatus,
|
|
155
|
+
content,
|
|
156
|
+
metadata: {
|
|
157
|
+
createdAt,
|
|
158
|
+
updatedAt: metadata?.updatedAt ?? undefined,
|
|
159
|
+
lastAccessedAt: metadata?.lastAccessedAt ?? undefined,
|
|
160
|
+
importance: clampUnitInterval(Number(metadata?.importance ?? 0.7), 0.7),
|
|
161
|
+
strength: Number(metadata?.strength ?? 0.5),
|
|
162
|
+
confidence:
|
|
163
|
+
metadata?.confidence == null
|
|
164
|
+
? undefined
|
|
165
|
+
: clampUnitInterval(Number(metadata.confidence), 1),
|
|
166
|
+
accessCount: Number(metadata?.accessCount ?? 0),
|
|
167
|
+
version: Number(metadata?.version ?? 1),
|
|
168
|
+
source: (metadata?.source ?? "assistant") as MemorySource,
|
|
169
|
+
tags: asStringArray(metadata?.tags),
|
|
170
|
+
entities: asStringArray(metadata?.entities),
|
|
171
|
+
supersededBy: metadata?.supersededBy ?? undefined,
|
|
172
|
+
relatedTo: asStringArray(metadata?.relatedTo),
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function buildWhere(
|
|
178
|
+
scope: LongTermMemoryScope,
|
|
179
|
+
options?: {
|
|
180
|
+
status?: MemoryStatus;
|
|
181
|
+
includeArchived?: boolean;
|
|
182
|
+
types?: MemoryType[];
|
|
183
|
+
},
|
|
184
|
+
): Where {
|
|
185
|
+
// Chroma v3: top-level `where` must have exactly one key (field predicate or $and/$or).
|
|
186
|
+
const parts: Where[] = [{ userId: scope.userId } as Where];
|
|
187
|
+
|
|
188
|
+
const statusFilter = options?.includeArchived
|
|
189
|
+
? options.status
|
|
190
|
+
: (options?.status ?? "active");
|
|
191
|
+
|
|
192
|
+
if (statusFilter !== undefined) {
|
|
193
|
+
parts.push({ status: statusFilter } as Where);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (options?.types?.length) {
|
|
197
|
+
parts.push({ type: { $in: options.types } } as Where);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (parts.length === 1) {
|
|
201
|
+
return parts[0]!;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return { $and: parts } as Where;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function chromaClientArgsFromUrl(urlString: string): {
|
|
208
|
+
host: string;
|
|
209
|
+
port?: number;
|
|
210
|
+
ssl: boolean;
|
|
211
|
+
} {
|
|
212
|
+
const url = new URL(urlString);
|
|
213
|
+
return {
|
|
214
|
+
host: url.hostname,
|
|
215
|
+
port: url.port ? Number(url.port) : undefined,
|
|
216
|
+
ssl: url.protocol === "https:",
|
|
217
|
+
};
|
|
218
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Agent, SummarizingConversationManager } from "@strands-agents/sdk";
|
|
2
|
+
import { SessionManager, FileStorage } from "@strands-agents/sdk";
|
|
3
|
+
import { sessionsPath } from "../../utils/paths";
|
|
4
|
+
|
|
5
|
+
export function create(sessionId: string) {
|
|
6
|
+
const sessionManager = new SessionManager({
|
|
7
|
+
sessionId,
|
|
8
|
+
storage: { snapshot: new FileStorage(sessionsPath()) },
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const conversationManager = new SummarizingConversationManager({
|
|
12
|
+
summaryRatio: 0.5,
|
|
13
|
+
preserveRecentMessages: 5,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
return { sessionManager, conversationManager };
|
|
17
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { anthropic, createAnthropic } from "@ai-sdk/anthropic";
|
|
2
|
+
import { VercelModel } from "@strands-agents/sdk/models/vercel";
|
|
3
|
+
import type { AnthropicProviderSettings } from "@ai-sdk/anthropic";
|
|
4
|
+
import type { VercelModelConfig } from "@strands-agents/sdk/models/vercel";
|
|
5
|
+
import { omit, pick } from "lodash";
|
|
6
|
+
|
|
7
|
+
const PROVIDER_SETTINGS_KEYS = [
|
|
8
|
+
"apiKey",
|
|
9
|
+
"authToken",
|
|
10
|
+
"baseURL",
|
|
11
|
+
"headers",
|
|
12
|
+
] as const;
|
|
13
|
+
|
|
14
|
+
function pickProviderSettings(
|
|
15
|
+
params: Record<string, unknown>,
|
|
16
|
+
): AnthropicProviderSettings {
|
|
17
|
+
const picked = pick(params, [...PROVIDER_SETTINGS_KEYS]) as Record<
|
|
18
|
+
string,
|
|
19
|
+
unknown
|
|
20
|
+
>;
|
|
21
|
+
const unset = Object.keys(picked).filter((k) => picked[k] === undefined);
|
|
22
|
+
return omit(picked, unset) as AnthropicProviderSettings;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function pickVercelModelConfig(
|
|
26
|
+
params: Record<string, unknown>,
|
|
27
|
+
): Partial<VercelModelConfig> {
|
|
28
|
+
return omit(params, [
|
|
29
|
+
...PROVIDER_SETTINGS_KEYS,
|
|
30
|
+
]) as Partial<VercelModelConfig>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Anthropic via AI SDK + Strands {@link VercelModel}.
|
|
35
|
+
*
|
|
36
|
+
* - **`config.llm.model`**: model id passed to `anthropic(...)` (e.g. `claude-sonnet-4-20250514`).
|
|
37
|
+
* - **`params`**: Settings {@link AnthropicProviderSettings} (`apiKey`, `authToken`, `baseURL`, …).
|
|
38
|
+
* If none are set, the default provider is used (`ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN` from env).
|
|
39
|
+
* - Any other `params` keys are forwarded as {@link VercelModelConfig} (e.g. `temperature`, `maxTokens`).
|
|
40
|
+
*/
|
|
41
|
+
export function create(
|
|
42
|
+
model: string,
|
|
43
|
+
params: Record<string, unknown> = {},
|
|
44
|
+
): VercelModel {
|
|
45
|
+
const settings = pickProviderSettings(params);
|
|
46
|
+
const provider =
|
|
47
|
+
Object.keys(settings).length > 0 ? createAnthropic(settings) : anthropic;
|
|
48
|
+
const config = pickVercelModelConfig(params);
|
|
49
|
+
return new VercelModel({
|
|
50
|
+
provider: provider(model),
|
|
51
|
+
...config,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { BedrockRuntimeClientConfig } from "@aws-sdk/client-bedrock-runtime";
|
|
2
|
+
import { BedrockModel } from "@strands-agents/sdk/models/bedrock";
|
|
3
|
+
import type { BedrockModelOptions } from "@strands-agents/sdk";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Params map from `config.llm.params` into {@link BedrockModel} options.
|
|
7
|
+
*
|
|
8
|
+
* - **`region`**: AWS region (default `us-west-2`).
|
|
9
|
+
* - **`clientConfig`**: passed to the Bedrock Runtime client (credentials, profile, etc.).
|
|
10
|
+
* See AWS SDK v3 docs for credential options.
|
|
11
|
+
* - **`apiKey`**: optional Bedrock API key auth (bearer), if used instead of SigV4.
|
|
12
|
+
* - Any other keys are forwarded as Bedrock model config (e.g. `maxTokens`, `temperature`,
|
|
13
|
+
* `stream`, `cacheConfig`).
|
|
14
|
+
*
|
|
15
|
+
* `config.llm.model` is always used as `modelId`.
|
|
16
|
+
*/
|
|
17
|
+
export type BedrockLlmParams = Omit<
|
|
18
|
+
BedrockModelOptions,
|
|
19
|
+
"modelId" | "region" | "clientConfig" | "apiKey"
|
|
20
|
+
> & {
|
|
21
|
+
region?: string;
|
|
22
|
+
clientConfig?: BedrockRuntimeClientConfig;
|
|
23
|
+
apiKey?: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const TOP_LEVEL_KEYS = new Set(["region", "clientConfig", "apiKey", "modelId"]);
|
|
27
|
+
|
|
28
|
+
export function create(
|
|
29
|
+
model: string,
|
|
30
|
+
params: Record<string, unknown> = {},
|
|
31
|
+
): BedrockModel {
|
|
32
|
+
const p = params as Record<string, unknown>;
|
|
33
|
+
const region =
|
|
34
|
+
typeof p.region === "string" && p.region.length > 0
|
|
35
|
+
? p.region
|
|
36
|
+
: "us-west-2";
|
|
37
|
+
const clientConfig = p.clientConfig as BedrockRuntimeClientConfig | undefined;
|
|
38
|
+
const apiKey = typeof p.apiKey === "string" ? p.apiKey : undefined;
|
|
39
|
+
|
|
40
|
+
const modelOptions: Record<string, unknown> = {};
|
|
41
|
+
for (const [key, value] of Object.entries(p)) {
|
|
42
|
+
if (!TOP_LEVEL_KEYS.has(key)) {
|
|
43
|
+
modelOptions[key] = value;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return new BedrockModel({
|
|
48
|
+
modelId: model,
|
|
49
|
+
region,
|
|
50
|
+
...(clientConfig ? { clientConfig } : {}),
|
|
51
|
+
...(apiKey !== undefined ? { apiKey } : {}),
|
|
52
|
+
...modelOptions,
|
|
53
|
+
} as BedrockModelOptions);
|
|
54
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { GoogleModel } from "@strands-agents/sdk/models/google";
|
|
2
|
+
import type { GoogleModelOptions } from "@strands-agents/sdk/models/google";
|
|
3
|
+
import { omit, pick } from "lodash";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Keys passed to {@link GoogleModel} alongside `modelId` / `params`.
|
|
7
|
+
* Generation options (`temperature`, `maxOutputTokens`, …) belong in `params`
|
|
8
|
+
* (see Gemini [GenerationConfig](https://ai.google.dev/api/generate-content#generationconfig)).
|
|
9
|
+
*/
|
|
10
|
+
const TOP_LEVEL_KEYS = [
|
|
11
|
+
"apiKey",
|
|
12
|
+
"client",
|
|
13
|
+
"clientConfig",
|
|
14
|
+
"builtInTools",
|
|
15
|
+
] as const;
|
|
16
|
+
|
|
17
|
+
function omitUndefined(
|
|
18
|
+
record: Record<string, unknown>,
|
|
19
|
+
): Record<string, unknown> {
|
|
20
|
+
const unset = Object.keys(record).filter((k) => record[k] === undefined);
|
|
21
|
+
return omit(record, unset);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Google Gemini via Strands {@link GoogleModel} (`@google/genai` under the hood).
|
|
26
|
+
*
|
|
27
|
+
* - **`config.llm.model`**: Gemini id (e.g. `gemini-2.5-flash`).
|
|
28
|
+
* - **`params`**: Optional `apiKey` (defaults to `GEMINI_API_KEY`), or `client` / `clientConfig`
|
|
29
|
+
* for a custom Google GenAI client. Any other keys are sent as `params` to the API
|
|
30
|
+
* (`temperature`, `maxOutputTokens`, `topP`, `topK`, …).
|
|
31
|
+
*/
|
|
32
|
+
export function create(
|
|
33
|
+
model: string,
|
|
34
|
+
params: Record<string, unknown> = {},
|
|
35
|
+
): GoogleModel {
|
|
36
|
+
const top = omitUndefined(
|
|
37
|
+
pick(params, [...TOP_LEVEL_KEYS]) as Record<string, unknown>,
|
|
38
|
+
) as Pick<
|
|
39
|
+
GoogleModelOptions,
|
|
40
|
+
"apiKey" | "client" | "clientConfig" | "builtInTools"
|
|
41
|
+
>;
|
|
42
|
+
const filtered = omitUndefined(
|
|
43
|
+
omit(params, [...TOP_LEVEL_KEYS]) as Record<string, unknown>,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return new GoogleModel({
|
|
47
|
+
modelId: model,
|
|
48
|
+
...top,
|
|
49
|
+
...(Object.keys(filtered).length > 0 ? { params: filtered } : {}),
|
|
50
|
+
} as GoogleModelOptions);
|
|
51
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Model, BaseModelConfig } from "@strands-agents/sdk";
|
|
2
|
+
|
|
3
|
+
export type ModelProvider = {
|
|
4
|
+
create: (
|
|
5
|
+
model: string,
|
|
6
|
+
params: Record<string, any>,
|
|
7
|
+
) => Model<BaseModelConfig>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const modelProviders: Record<string, () => Promise<ModelProvider>> = {
|
|
11
|
+
anthropic: () => import("./anthropic.ts"),
|
|
12
|
+
bedrock: () => import("./bedrock.ts"),
|
|
13
|
+
google: () => import("./google.ts"),
|
|
14
|
+
ollama: () => import("./ollama/index.ts"),
|
|
15
|
+
openai: () => import("./openai.ts"),
|
|
16
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { OllamaModelConfig } from "./strands-ollama.ts";
|
|
2
|
+
import { StrandsOllamaModel } from "./strands-ollama.ts";
|
|
3
|
+
|
|
4
|
+
/** Strands {@link Model} backed by local (or remote) Ollama via `ollama` JS. */
|
|
5
|
+
export function create(
|
|
6
|
+
model: string,
|
|
7
|
+
params: Record<string, any>,
|
|
8
|
+
): StrandsOllamaModel {
|
|
9
|
+
return new StrandsOllamaModel({
|
|
10
|
+
modelId: model,
|
|
11
|
+
...(params as Omit<OllamaModelConfig, "modelId">),
|
|
12
|
+
});
|
|
13
|
+
}
|