mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-04-25 08:26:22 +02:00
allow provider / model config
This commit is contained in:
parent
62caa0c8b6
commit
6251c8f007
9 changed files with 140 additions and 12 deletions
|
|
@ -1,5 +1,4 @@
|
|||
import { streamText, ModelMessage, tool, stepCountIs } from "ai";
|
||||
import { openai } from "@ai-sdk/openai";
|
||||
import * as readline from "readline/promises";
|
||||
import { stdin as input, stdout as output } from "process";
|
||||
import { z } from "zod";
|
||||
|
|
@ -10,8 +9,9 @@ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
|
|||
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
||||
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
||||
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
||||
import { getProvider } from "../lib/models.js";
|
||||
import { DefaultModel } from "../config/config.js";
|
||||
|
||||
const model = openai("gpt-4.1");
|
||||
const rl = readline.createInterface({ input, output });
|
||||
|
||||
// Base directory for file operations - dynamically use user's home directory
|
||||
|
|
@ -57,8 +57,9 @@ export async function startCopilot() {
|
|||
process.stdout.write("\nCopilot: ");
|
||||
|
||||
let currentStep = 0;
|
||||
const provider = getProvider();
|
||||
const result = streamText({
|
||||
model: model,
|
||||
model: provider(DefaultModel),
|
||||
messages: messages,
|
||||
system: `You are an intelligent workflow assistant helping users manage their workflows in ${BASE_DIR}.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import path from "path";
|
||||
import fs from "fs";
|
||||
import { McpServerConfig } from "../entities/mcp.js";
|
||||
import { ModelConfig } from "../entities/models.js";
|
||||
import { z } from "zod";
|
||||
import { homedir } from "os";
|
||||
|
||||
// Resolve app root relative to compiled file location (dist/...)
|
||||
export const WorkDir = path.join(homedir(), ".rowboat");
|
||||
|
||||
const baseMcpConfig = {
|
||||
const baseMcpConfig: z.infer<typeof McpServerConfig> = {
|
||||
mcpServers: {
|
||||
firecrawl: {
|
||||
command: "npx",
|
||||
|
|
@ -23,7 +24,19 @@ const baseMcpConfig = {
|
|||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const baseModelConfig: z.infer<typeof ModelConfig> = {
|
||||
providers: {
|
||||
openai: {
|
||||
flavor: "openai",
|
||||
},
|
||||
},
|
||||
defaults: {
|
||||
provider: "openai",
|
||||
model: "gpt-4.1",
|
||||
}
|
||||
};
|
||||
|
||||
function ensureMcpConfig() {
|
||||
const configPath = path.join(WorkDir, "config", "mcp.json");
|
||||
|
|
@ -32,6 +45,13 @@ function ensureMcpConfig() {
|
|||
}
|
||||
}
|
||||
|
||||
function ensureModelConfig() {
|
||||
const configPath = path.join(WorkDir, "config", "models.json");
|
||||
if (!fs.existsSync(configPath)) {
|
||||
fs.writeFileSync(configPath, JSON.stringify(baseModelConfig, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
function ensureDirs() {
|
||||
const ensure = (p: string) => { if (!fs.existsSync(p)) fs.mkdirSync(p, { recursive: true }); };
|
||||
ensure(WorkDir);
|
||||
|
|
@ -39,6 +59,7 @@ function ensureDirs() {
|
|||
ensure(path.join(WorkDir, "agents"));
|
||||
ensure(path.join(WorkDir, "config"));
|
||||
ensureMcpConfig();
|
||||
ensureModelConfig();
|
||||
}
|
||||
|
||||
ensureDirs();
|
||||
|
|
@ -50,5 +71,16 @@ function loadMcpServerConfig(): z.infer<typeof McpServerConfig> {
|
|||
return McpServerConfig.parse(JSON.parse(config));
|
||||
}
|
||||
|
||||
function loadModelConfig(): z.infer<typeof ModelConfig> {
|
||||
const configPath = path.join(WorkDir, "config", "models.json");
|
||||
if (!fs.existsSync(configPath)) return baseModelConfig;
|
||||
const config = fs.readFileSync(configPath, "utf8");
|
||||
return ModelConfig.parse(JSON.parse(config));
|
||||
}
|
||||
|
||||
const { mcpServers } = loadMcpServerConfig();
|
||||
const { providers, defaults } = loadModelConfig();
|
||||
export const McpServers = mcpServers;
|
||||
export const Providers = providers;
|
||||
export const DefaultModel = defaults.model;
|
||||
export const DefaultProvider = defaults.provider;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ export const AgentTool = z.discriminatedUnion("type", [
|
|||
|
||||
export const Agent = z.object({
|
||||
name: z.string(),
|
||||
model: z.string(),
|
||||
provider: z.string().optional(),
|
||||
model: z.string().optional(),
|
||||
description: z.string(),
|
||||
instructions: z.string(),
|
||||
tools: z.record(z.string(), AgentTool).optional(),
|
||||
|
|
|
|||
15
apps/cli/src/application/entities/models.ts
Normal file
15
apps/cli/src/application/entities/models.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import z from "zod";
|
||||
|
||||
export const Provider = z.object({
|
||||
flavor: z.enum(["openai", "anthropic", "google"]),
|
||||
apiKey: z.string().optional(),
|
||||
baseURL: z.string().optional(),
|
||||
});
|
||||
|
||||
export const ModelConfig = z.object({
|
||||
providers: z.record(z.string(), Provider),
|
||||
defaults: z.object({
|
||||
provider: z.string(),
|
||||
model: z.string(),
|
||||
}),
|
||||
});
|
||||
|
|
@ -1,14 +1,13 @@
|
|||
import { Message, MessageList } from "../entities/message.js";
|
||||
import { z } from "zod";
|
||||
import { Step, StepInputT, StepOutputT } from "./step.js";
|
||||
import { openai } from "@ai-sdk/openai";
|
||||
import { google } from "@ai-sdk/google";
|
||||
import { generateText, ModelMessage, stepCountIs, streamText, tool, Tool, ToolSet, jsonSchema } from "ai";
|
||||
import { ModelMessage, stepCountIs, streamText, tool, Tool, ToolSet, jsonSchema } from "ai";
|
||||
import { Agent, AgentTool } from "../entities/agent.js";
|
||||
import { WorkDir } from "../config/config.js";
|
||||
import { DefaultModel, WorkDir } from "../config/config.js";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { loadWorkflow } from "./utils.js";
|
||||
import { getProvider } from "./models.js";
|
||||
|
||||
const BashTool = tool({
|
||||
description: "Run a command in the shell",
|
||||
|
|
@ -157,9 +156,9 @@ export class AgentNode implements Step {
|
|||
|
||||
// console.log("\n\n\t>>>>\t\ttools", JSON.stringify(tools, null, 2));
|
||||
|
||||
const provider = getProvider(this.agent.provider);
|
||||
const { fullStream } = streamText({
|
||||
model: openai("gpt-4.1"),
|
||||
// model: google("gemini-2.5-flash"),
|
||||
model: provider(this.agent.model || DefaultModel),
|
||||
messages: convertFromMessages(input),
|
||||
system: this.agent.instructions,
|
||||
stopWhen: stepCountIs(1),
|
||||
|
|
|
|||
40
apps/cli/src/application/lib/models.ts
Normal file
40
apps/cli/src/application/lib/models.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { createOpenAI, OpenAIProvider } from "@ai-sdk/openai";
|
||||
import { createGoogleGenerativeAI, GoogleGenerativeAIProvider } from "@ai-sdk/google";
|
||||
import { AnthropicProvider, createAnthropic } from "@ai-sdk/anthropic";
|
||||
import { DefaultModel, DefaultProvider, Providers } from "../config/config.js";
|
||||
|
||||
const providerMap: Record<string, OpenAIProvider | GoogleGenerativeAIProvider | AnthropicProvider> = {};
|
||||
|
||||
export function getProvider(name: string = "") {
|
||||
if (!name) {
|
||||
name = DefaultProvider;
|
||||
}
|
||||
if (providerMap[name]) {
|
||||
return providerMap[name];
|
||||
}
|
||||
const providerConfig = Providers[name];
|
||||
if (!providerConfig) {
|
||||
throw new Error(`Provider ${name} not found`);
|
||||
}
|
||||
switch (providerConfig.flavor) {
|
||||
case "openai":
|
||||
providerMap[name] = createOpenAI({
|
||||
apiKey: providerConfig.apiKey,
|
||||
baseURL: providerConfig.baseURL,
|
||||
});
|
||||
break;
|
||||
case "anthropic":
|
||||
providerMap[name] = createAnthropic({
|
||||
apiKey: providerConfig.apiKey,
|
||||
baseURL: providerConfig.baseURL,
|
||||
});
|
||||
break;
|
||||
case "google":
|
||||
providerMap[name] = createGoogleGenerativeAI({
|
||||
apiKey: providerConfig.apiKey,
|
||||
baseURL: providerConfig.baseURL,
|
||||
});
|
||||
break;
|
||||
}
|
||||
return providerMap[name];
|
||||
}
|
||||
5
apps/cli/src/test.ts
Normal file
5
apps/cli/src/test.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { RunEvent } from "./application/entities/workflow-event.js";
|
||||
|
||||
const obj = {"type":"tool-invocation","stepId":"test_agent","toolName":"ask-human","input":{"question":"Do you want me to run the command `date` in the terminal to show today’s date?"},"ts":"2025-11-11T06:31:20.103Z"};
|
||||
|
||||
console.log(RunEvent.parse(obj));
|
||||
Loading…
Add table
Add a link
Reference in a new issue