@m8i-51/shoal 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/bin/init.js +17 -0
- package/framework/cost.ts +15 -0
- package/framework/llm-client.ts +28 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -215,12 +215,15 @@ shoal defaults to Anthropic Claude. To use a different provider, set these varia
|
|
|
215
215
|
| Provider | Variables |
|
|
216
216
|
|---|---|
|
|
217
217
|
| Anthropic (default) | `ANTHROPIC_API_KEY` |
|
|
218
|
+
| Amazon Bedrock | `LLM_PROVIDER=bedrock`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION` |
|
|
218
219
|
| OpenAI | `LLM_PROVIDER=openai`, `LLM_API_KEY`, `LLM_MODEL` |
|
|
219
220
|
| OpenRouter | `LLM_PROVIDER=openrouter`, `LLM_API_KEY`, `LLM_MODEL` |
|
|
220
221
|
| Codex (ChatGPT subscription) | run `npm run auth:codex` once, then `LLM_PROVIDER=codex` |
|
|
221
222
|
| Ollama | `LLM_BASE_URL=http://localhost:11434/v1`, `LLM_MODEL` |
|
|
222
223
|
| LM Studio | `LLM_BASE_URL=http://localhost:1234/v1`, `LLM_MODEL` |
|
|
223
224
|
|
|
225
|
+
For Bedrock, set `LLM_MODEL` to a Bedrock model ID such as `anthropic.claude-3-5-sonnet-20241022-v2:0`. Cross-region inference profiles (e.g. `us.anthropic.claude-3-5-sonnet-20241022-v2:0`) are also supported.
|
|
226
|
+
|
|
224
227
|
See `.env.example` for full examples.
|
|
225
228
|
|
|
226
229
|
---
|
package/bin/init.js
CHANGED
|
@@ -4,6 +4,7 @@ import { join } from "path";
|
|
|
4
4
|
|
|
5
5
|
const PROVIDERS = [
|
|
6
6
|
{ value: "anthropic", label: "Anthropic (Claude)", hint: "recommended", defaultModel: "claude-haiku-4-5-20251001" },
|
|
7
|
+
{ value: "bedrock", label: "Amazon Bedrock", hint: "AWS credentials", defaultModel: "anthropic.claude-3-5-haiku-20241022-v1:0" },
|
|
7
8
|
{ value: "openai", label: "OpenAI", defaultModel: "gpt-4o-mini" },
|
|
8
9
|
{ value: "groq", label: "Groq", hint: "free tier available", defaultModel: "llama-3.3-70b-versatile" },
|
|
9
10
|
{ value: "gemini", label: "Gemini", hint: "free tier available", defaultModel: "gemini-2.0-flash" },
|
|
@@ -47,6 +48,22 @@ export async function runInit(cwd) {
|
|
|
47
48
|
placeholder: "sk-ant-...",
|
|
48
49
|
validate: (v) => v?.trim() ? undefined : "Required",
|
|
49
50
|
}));
|
|
51
|
+
} else if (provider === "bedrock") {
|
|
52
|
+
env.LLM_PROVIDER = "bedrock";
|
|
53
|
+
env.AWS_ACCESS_KEY_ID = guard(await text({
|
|
54
|
+
message: "AWS_ACCESS_KEY_ID",
|
|
55
|
+
placeholder: "AKIA...",
|
|
56
|
+
validate: (v) => v?.trim() ? undefined : "Required",
|
|
57
|
+
}));
|
|
58
|
+
env.AWS_SECRET_ACCESS_KEY = guard(await text({
|
|
59
|
+
message: "AWS_SECRET_ACCESS_KEY",
|
|
60
|
+
placeholder: "...",
|
|
61
|
+
validate: (v) => v?.trim() ? undefined : "Required",
|
|
62
|
+
}));
|
|
63
|
+
env.AWS_REGION = guard(await text({
|
|
64
|
+
message: "AWS region",
|
|
65
|
+
defaultValue: "us-east-1",
|
|
66
|
+
}));
|
|
50
67
|
} else if (provider === "ollama") {
|
|
51
68
|
env.LLM_PROVIDER = "ollama";
|
|
52
69
|
const baseUrl = guard(await text({
|
package/framework/cost.ts
CHANGED
|
@@ -9,6 +9,14 @@ const ANTHROPIC_PRICING: Record<string, { input: number; output: number }> = {
|
|
|
9
9
|
"claude-3-opus-20240229": { input: 15 / 1e6, output: 75 / 1e6 },
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
// Bedrock on-demand pricing (us-east-1, as of 2026-04)
|
|
13
|
+
const BEDROCK_PRICING: Record<string, { input: number; output: number }> = {
|
|
14
|
+
"anthropic.claude-3-5-sonnet-20241022-v2:0": { input: 3 / 1e6, output: 15 / 1e6 },
|
|
15
|
+
"anthropic.claude-3-5-haiku-20241022-v1:0": { input: 0.8 / 1e6, output: 4 / 1e6 },
|
|
16
|
+
"anthropic.claude-3-opus-20240229-v1:0": { input: 15 / 1e6, output: 75 / 1e6 },
|
|
17
|
+
"anthropic.claude-3-haiku-20240307-v1:0": { input: 0.25 / 1e6, output: 1.25 / 1e6 },
|
|
18
|
+
};
|
|
19
|
+
|
|
12
20
|
const OPENAI_PRICING: Record<string, { input: number; output: number }> = {
|
|
13
21
|
"gpt-4o": { input: 5 / 1e6, output: 15 / 1e6 },
|
|
14
22
|
"gpt-4o-mini": { input: 0.15 / 1e6, output: 0.6 / 1e6 },
|
|
@@ -71,6 +79,13 @@ export async function estimateCost(
|
|
|
71
79
|
const key = Object.keys(ANTHROPIC_PRICING).find((k) => model.startsWith(k));
|
|
72
80
|
if (key) pricing = ANTHROPIC_PRICING[key];
|
|
73
81
|
}
|
|
82
|
+
} else if (provider === "bedrock") {
|
|
83
|
+
pricing = BEDROCK_PRICING[model];
|
|
84
|
+
if (!pricing) {
|
|
85
|
+
// cross-region prefix (e.g. "us.anthropic.claude-..." → strip prefix)
|
|
86
|
+
const stripped = model.replace(/^[a-z]{2}\./, "");
|
|
87
|
+
pricing = BEDROCK_PRICING[stripped];
|
|
88
|
+
}
|
|
74
89
|
} else if (provider === "openai") {
|
|
75
90
|
pricing = OPENAI_PRICING[model];
|
|
76
91
|
} else if (provider === "openrouter") {
|
package/framework/llm-client.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import Anthropic from "@anthropic-ai/sdk";
|
|
13
|
+
import AnthropicBedrock from "@anthropic-ai/bedrock-sdk";
|
|
13
14
|
import OpenAI from "openai";
|
|
14
15
|
import * as fs from "fs";
|
|
15
16
|
import * as os from "os";
|
|
@@ -46,6 +47,21 @@ class AnthropicClient {
|
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
// ---- Amazon Bedrock クライアント ----
|
|
51
|
+
|
|
52
|
+
class BedrockClient {
|
|
53
|
+
private client: AnthropicBedrock;
|
|
54
|
+
|
|
55
|
+
constructor() {
|
|
56
|
+
// AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_REGION を自動読み取り
|
|
57
|
+
this.client = new AnthropicBedrock();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async createMessage(params: CreateMessageParams): Promise<Message> {
|
|
61
|
+
return this.client.messages.create(params) as Promise<Message>;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
49
65
|
// ---- OpenAI 互換クライアント ----
|
|
50
66
|
|
|
51
67
|
function toOpenAITools(tools: Tool[]): OpenAI.ChatCompletionTool[] {
|
|
@@ -452,7 +468,7 @@ class CodexClient {
|
|
|
452
468
|
|
|
453
469
|
// ---- Factory ----
|
|
454
470
|
|
|
455
|
-
export type LLMClient = AnthropicClient | OpenAICompatClient | CodexClient;
|
|
471
|
+
export type LLMClient = AnthropicClient | BedrockClient | OpenAICompatClient | CodexClient;
|
|
456
472
|
|
|
457
473
|
// OpenAI-compat プロバイダのデフォルト設定
|
|
458
474
|
// LLM_BASE_URL / LLM_MODEL で個別上書き可能
|
|
@@ -470,6 +486,17 @@ export function createLLMClient(): { client: LLMClient; defaultModel: string; pr
|
|
|
470
486
|
const baseURL = process.env.LLM_BASE_URL;
|
|
471
487
|
const model = process.env.LLM_MODEL;
|
|
472
488
|
|
|
489
|
+
// Bedrock
|
|
490
|
+
if (provider === "bedrock") {
|
|
491
|
+
const effectiveModel = model ?? "anthropic.claude-3-5-haiku-20241022-v1:0";
|
|
492
|
+
console.log(`[LLM] provider: Amazon Bedrock (region: ${process.env.AWS_REGION ?? "us-east-1"}), model: ${effectiveModel}`);
|
|
493
|
+
return {
|
|
494
|
+
client: new BedrockClient(),
|
|
495
|
+
defaultModel: effectiveModel,
|
|
496
|
+
provider: "bedrock",
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
|
|
473
500
|
// Codex は独自クライアント
|
|
474
501
|
if (provider === "codex") {
|
|
475
502
|
const effectiveModel = model ?? "gpt-5.1-codex-mini";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m8i-51/shoal",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Multi-agent web exploration framework — finds bugs, UX issues, and missing features by running AI agents against your app",
|
|
6
6
|
"repository": {
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"test:watch": "vitest"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
+
"@anthropic-ai/bedrock-sdk": "^0.29.1",
|
|
37
38
|
"@anthropic-ai/sdk": "^0.91.1",
|
|
38
39
|
"@clack/prompts": "^1.3.0",
|
|
39
40
|
"dotenv": "^17.3.1",
|