mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-12 00:32:38 +02:00
Merge main into cli-to-dev
This commit is contained in:
commit
ea4159a94b
3 changed files with 56 additions and 38 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@rowboatlabs/rowboatx",
|
"name": "@rowboatlabs/rowboatx",
|
||||||
"version": "0.12.0",
|
"version": "0.13.0",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { ToolCallPart } from "./application/entities/message.js";
|
||||||
import { Agent } from "./application/entities/agent.js";
|
import { Agent } from "./application/entities/agent.js";
|
||||||
import { McpServerConfig, McpServerDefinition } from "./application/entities/mcp.js";
|
import { McpServerConfig, McpServerDefinition } from "./application/entities/mcp.js";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { Flavor, ModelConfig } from "./application/entities/models.js";
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
@ -225,15 +226,15 @@ export async function modelConfig() {
|
||||||
|
|
||||||
const rl = createInterface({ input, output });
|
const rl = createInterface({ input, output });
|
||||||
try {
|
try {
|
||||||
const flavors = [
|
const defaultApiKeyEnvVars: Record<z.infer<typeof Flavor>, string> = {
|
||||||
"openai",
|
openai: "OPENAI_API_KEY",
|
||||||
"anthropic",
|
anthropic: "ANTHROPIC_API_KEY",
|
||||||
"google",
|
google: "GOOGLE_GENERATIVE_AI_API_KEY",
|
||||||
"ollama",
|
ollama: "",
|
||||||
"openai-compatible",
|
"openai-compatible": "",
|
||||||
"openrouter",
|
openrouter: "",
|
||||||
] as const;
|
};
|
||||||
const defaultBaseUrls: Record<(typeof flavors)[number], string> = {
|
const defaultBaseUrls: Record<z.infer<typeof Flavor>, string> = {
|
||||||
openai: "https://api.openai.com/v1",
|
openai: "https://api.openai.com/v1",
|
||||||
anthropic: "https://api.anthropic.com/v1",
|
anthropic: "https://api.anthropic.com/v1",
|
||||||
google: "https://generativelanguage.googleapis.com/v1beta",
|
google: "https://generativelanguage.googleapis.com/v1beta",
|
||||||
|
|
@ -241,7 +242,7 @@ export async function modelConfig() {
|
||||||
"openai-compatible": "http://localhost:8080/v1",
|
"openai-compatible": "http://localhost:8080/v1",
|
||||||
openrouter: "https://openrouter.ai/api/v1",
|
openrouter: "https://openrouter.ai/api/v1",
|
||||||
};
|
};
|
||||||
const defaultModels: Record<(typeof flavors)[number], string> = {
|
const defaultModels: Record<z.infer<typeof Flavor>, string> = {
|
||||||
openai: "gpt-5.1",
|
openai: "gpt-5.1",
|
||||||
anthropic: "claude-sonnet-4-5",
|
anthropic: "claude-sonnet-4-5",
|
||||||
google: "gemini-2.5-pro",
|
google: "gemini-2.5-pro",
|
||||||
|
|
@ -257,25 +258,22 @@ export async function modelConfig() {
|
||||||
renderCurrentModel(currentProvider || "none", currentProviderConfig?.flavor || "", currentModel || "none");
|
renderCurrentModel(currentProvider || "none", currentProviderConfig?.flavor || "", currentModel || "none");
|
||||||
}
|
}
|
||||||
|
|
||||||
const flavorPromptLines = flavors
|
const FlavorList = [...Flavor.options];
|
||||||
|
const flavorPromptLines = FlavorList
|
||||||
.map((f, idx) => ` ${idx + 1}. ${f}`)
|
.map((f, idx) => ` ${idx + 1}. ${f}`)
|
||||||
.join("\n");
|
.join("\n");
|
||||||
const flavorAnswer = await rl.question(
|
const flavorAnswer = await rl.question(
|
||||||
`Select a provider type:\n${flavorPromptLines}\nEnter number or name` +
|
`Select a provider type:\n${flavorPromptLines}\nEnter number or name: `
|
||||||
(currentProvider ? ` [${currentProvider}]` : "") +
|
|
||||||
": ",
|
|
||||||
);
|
);
|
||||||
let selectedFlavorRaw = flavorAnswer.trim();
|
let selectedFlavorRaw = flavorAnswer.trim();
|
||||||
let selectedFlavor: (typeof flavors)[number] | null = null;
|
let selectedFlavor: z.infer<typeof Flavor> | null = null;
|
||||||
if (selectedFlavorRaw === "" && currentProvider && (flavors as readonly string[]).includes(currentProvider)) {
|
if (/^\d+$/.test(selectedFlavorRaw)) {
|
||||||
selectedFlavor = currentProvider as (typeof flavors)[number];
|
|
||||||
} else if (/^\d+$/.test(selectedFlavorRaw)) {
|
|
||||||
const idx = parseInt(selectedFlavorRaw, 10) - 1;
|
const idx = parseInt(selectedFlavorRaw, 10) - 1;
|
||||||
if (idx >= 0 && idx < flavors.length) {
|
if (idx >= 0 && idx < FlavorList.length) {
|
||||||
selectedFlavor = flavors[idx];
|
selectedFlavor = FlavorList[idx];
|
||||||
}
|
}
|
||||||
} else if ((flavors as readonly string[]).includes(selectedFlavorRaw)) {
|
} else if (FlavorList.includes(selectedFlavorRaw as z.infer<typeof Flavor>)) {
|
||||||
selectedFlavor = selectedFlavorRaw as (typeof flavors)[number];
|
selectedFlavor = selectedFlavorRaw as z.infer<typeof Flavor>;
|
||||||
}
|
}
|
||||||
if (!selectedFlavor) {
|
if (!selectedFlavor) {
|
||||||
console.error("Invalid selection. Exiting.");
|
console.error("Invalid selection. Exiting.");
|
||||||
|
|
@ -344,21 +342,38 @@ export async function modelConfig() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const headers: Record<string, string> = {};
|
||||||
|
|
||||||
const providerNameAns = await rl.question(
|
const providerNameAns = await rl.question(
|
||||||
`Enter a name/alias for this provider [${selectedFlavor}]: `,
|
`Enter a name/alias for this provider [${selectedFlavor}]: `,
|
||||||
);
|
);
|
||||||
providerName = providerNameAns.trim() || selectedFlavor;
|
providerName = providerNameAns.trim() || selectedFlavor;
|
||||||
|
|
||||||
const baseUrlDefault = defaultBaseUrls[selectedFlavor] || "";
|
|
||||||
const baseUrlAns = await rl.question(
|
const baseUrlAns = await rl.question(
|
||||||
`Enter baseURL for ${selectedFlavor} [${baseUrlDefault}]: `,
|
`Enter baseURL for ${selectedFlavor} [${defaultBaseUrls[selectedFlavor]}]: `,
|
||||||
);
|
);
|
||||||
const baseURL = (baseUrlAns.trim() || baseUrlDefault) || undefined;
|
const baseURL = baseUrlAns.trim() || undefined;
|
||||||
|
|
||||||
const apiKeyAns = await rl.question(
|
let apiKey: string | undefined = undefined;
|
||||||
`Enter API key for ${selectedFlavor} (leave blank to skip): `,
|
if (selectedFlavor !== "ollama") {
|
||||||
);
|
let autopickText = "";
|
||||||
const apiKey = apiKeyAns.trim() || undefined;
|
if (defaultApiKeyEnvVars[selectedFlavor]) {
|
||||||
|
autopickText = ` (leave blank to pick from environment variable ${defaultApiKeyEnvVars[selectedFlavor]})`;
|
||||||
|
}
|
||||||
|
const apiKeyAns = await rl.question(
|
||||||
|
`Enter API key for ${selectedFlavor}${autopickText}: `,
|
||||||
|
);
|
||||||
|
apiKey = apiKeyAns.trim() || undefined;
|
||||||
|
}
|
||||||
|
if (selectedFlavor === "ollama") {
|
||||||
|
const keyAns = await rl.question(
|
||||||
|
`Enter API key for ${selectedFlavor} (optional): `
|
||||||
|
);
|
||||||
|
const key = keyAns.trim();
|
||||||
|
if (key) {
|
||||||
|
headers["Authorization"] = `Bearer ${key}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const modelDefault = defaultModels[selectedFlavor];
|
const modelDefault = defaultModels[selectedFlavor];
|
||||||
const modelAns = await rl.question(
|
const modelAns = await rl.question(
|
||||||
|
|
@ -372,6 +387,7 @@ export async function modelConfig() {
|
||||||
flavor: selectedFlavor,
|
flavor: selectedFlavor,
|
||||||
...(apiKey ? { apiKey } : {}),
|
...(apiKey ? { apiKey } : {}),
|
||||||
...(baseURL ? { baseURL } : {}),
|
...(baseURL ? { baseURL } : {}),
|
||||||
|
...(headers ? { headers } : {}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const newConfig = {
|
const newConfig = {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
|
export const Flavor = z.enum([
|
||||||
|
"anthropic",
|
||||||
|
"google",
|
||||||
|
"ollama",
|
||||||
|
"openai",
|
||||||
|
"openai-compatible",
|
||||||
|
"openrouter",
|
||||||
|
]);
|
||||||
|
|
||||||
export const Provider = z.object({
|
export const Provider = z.object({
|
||||||
flavor: z.enum([
|
flavor: Flavor,
|
||||||
"anthropic",
|
|
||||||
"google",
|
|
||||||
"ollama",
|
|
||||||
"openai",
|
|
||||||
"openai-compatible",
|
|
||||||
"openrouter",
|
|
||||||
]),
|
|
||||||
apiKey: z.string().optional(),
|
apiKey: z.string().optional(),
|
||||||
baseURL: z.string().optional(),
|
baseURL: z.string().optional(),
|
||||||
headers: z.record(z.string(), z.string()).optional(),
|
headers: z.record(z.string(), z.string()).optional(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue