@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 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") {
@@ -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.5",
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",