@moikapy/origen 0.3.0 → 0.4.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/dist/adapter.d.ts +204 -0
- package/dist/adapter.js +21 -0
- package/dist/adapter.js.map +1 -0
- package/dist/chunk-ECRY7XDR.js +109 -0
- package/dist/chunk-ECRY7XDR.js.map +1 -0
- package/dist/chunk-TECUAB3E.js +296 -0
- package/dist/chunk-TECUAB3E.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +130 -0
- package/dist/index.js.map +1 -0
- package/dist/models.d.ts +34 -0
- package/dist/models.js +21 -0
- package/dist/models.js.map +1 -0
- package/dist/soul.d.ts +122 -0
- package/{src/soul.ts → dist/soul.js} +92 -268
- package/dist/soul.js.map +1 -0
- package/package.json +26 -8
- package/bun.lock +0 -561
- package/src/adapter.ts +0 -364
- package/src/agent.ts +0 -219
- package/src/index.ts +0 -58
- package/src/models.ts +0 -143
- package/src/types.ts +0 -59
- package/test/origen.test.ts +0 -171
- package/tsconfig.json +0 -17
package/src/models.ts
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Origen model configuration.
|
|
3
|
-
*
|
|
4
|
-
* Delegates to pi-ai's model registry for known providers (OpenRouter, Anthropic, Google, etc.)
|
|
5
|
-
* Plus custom entries for Ollama and free-tier aliases.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { getModel } from "@mariozechner/pi-ai";
|
|
9
|
-
import type { Model, Api } from "@mariozechner/pi-ai";
|
|
10
|
-
export type { Model as ProviderModel, Api } from "@mariozechner/pi-ai";
|
|
11
|
-
|
|
12
|
-
// ── Model registry ────────────────────────────────────────────────────
|
|
13
|
-
|
|
14
|
-
export interface ModelConfig {
|
|
15
|
-
name: string;
|
|
16
|
-
description: string;
|
|
17
|
-
free: boolean;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** UI-facing model config — safe to send to the client. Strips internal fields. */
|
|
21
|
-
export type UIModelConfig = ModelConfig;
|
|
22
|
-
|
|
23
|
-
/** Get models as a simple UI map (name, description, free). No internal fields. */
|
|
24
|
-
export function getModelsForUI(): Record<string, UIModelConfig> {
|
|
25
|
-
const uiModels: Record<string, UIModelConfig> = {};
|
|
26
|
-
for (const [id, config] of Object.entries(MODELS)) {
|
|
27
|
-
uiModels[id] = { name: config.name, description: config.description, free: config.free };
|
|
28
|
-
}
|
|
29
|
-
return uiModels;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Build MODELS map from pi-ai registry + custom entries
|
|
33
|
-
function buildModels(): Record<string, ModelConfig> {
|
|
34
|
-
const models: Record<string, ModelConfig> = {};
|
|
35
|
-
|
|
36
|
-
// ── OpenRouter (free tier) ───────────────────────────
|
|
37
|
-
models["openrouter/free"] = {
|
|
38
|
-
name: "Free (Auto)",
|
|
39
|
-
description: "Free — auto-selects best free model for your request",
|
|
40
|
-
free: true,
|
|
41
|
-
};
|
|
42
|
-
models["google/gemma-4-31b-it:free"] = {
|
|
43
|
-
name: "Gemma 4 31B",
|
|
44
|
-
description: "Free — great quality for Bible study",
|
|
45
|
-
free: true,
|
|
46
|
-
};
|
|
47
|
-
models["nvidia/nemotron-3-super-120b-a12b:free"] = {
|
|
48
|
-
name: "Nemotron 3 Super",
|
|
49
|
-
description: "Free — large model, strong reasoning",
|
|
50
|
-
free: true,
|
|
51
|
-
};
|
|
52
|
-
models["deepseek/deepseek-r1:free"] = {
|
|
53
|
-
name: "DeepSeek R1 (Free)",
|
|
54
|
-
description: "Free — reasoning with thinking support",
|
|
55
|
-
free: true,
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
models["qwen/qwen3-coder:free"] = {
|
|
59
|
-
name: "Qwen3 Coder",
|
|
60
|
-
description: "Free — 480B parameters, excellent tool use",
|
|
61
|
-
free: true,
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
// ── OpenRouter (premium) ─────────────────────────────
|
|
65
|
-
models["openrouter/auto"] = {
|
|
66
|
-
name: "Auto (All)",
|
|
67
|
-
description: "Auto-selects best model (requires credits)",
|
|
68
|
-
free: false,
|
|
69
|
-
};
|
|
70
|
-
models["anthropic/claude-sonnet-4"] = {
|
|
71
|
-
name: "Claude Sonnet 4",
|
|
72
|
-
description: "Premium — excellent quality + reasoning (requires credits)",
|
|
73
|
-
free: false,
|
|
74
|
-
};
|
|
75
|
-
models["google/gemini-2.5-flash-preview"] = {
|
|
76
|
-
name: "Gemini 2.5 Flash",
|
|
77
|
-
description: "Premium — fast with thinking (requires credits)",
|
|
78
|
-
free: false,
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
// ── Ollama (local, always free) ──────────────────────
|
|
82
|
-
models["ollama/llama3"] = {
|
|
83
|
-
name: "Llama 3 (Ollama)",
|
|
84
|
-
description: "Local — Meta's Llama 3, requires Ollama",
|
|
85
|
-
free: true,
|
|
86
|
-
};
|
|
87
|
-
models["ollama/gemma3"] = {
|
|
88
|
-
name: "Gemma 3 (Ollama)",
|
|
89
|
-
description: "Local — Google's Gemma 3, requires Ollama",
|
|
90
|
-
free: true,
|
|
91
|
-
};
|
|
92
|
-
models["ollama/mistral"] = {
|
|
93
|
-
name: "Mistral (Ollama)",
|
|
94
|
-
description: "Local — Mistral's 7B model, requires Ollama",
|
|
95
|
-
free: true,
|
|
96
|
-
};
|
|
97
|
-
models["ollama/qwen3"] = {
|
|
98
|
-
name: "Qwen 3 (Ollama)",
|
|
99
|
-
description: "Local — Alibaba's Qwen 3, requires Ollama",
|
|
100
|
-
free: true,
|
|
101
|
-
};
|
|
102
|
-
models["ollama/deepseek-r1"] = {
|
|
103
|
-
name: "DeepSeek R1 (Ollama)",
|
|
104
|
-
description: "Local — reasoning model, requires Ollama",
|
|
105
|
-
free: true,
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
return models;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export const MODELS: Record<string, ModelConfig> = buildModels();
|
|
112
|
-
export type ModelId = keyof typeof MODELS;
|
|
113
|
-
|
|
114
|
-
/** Default model — free router, works with $0 credits */
|
|
115
|
-
export const DEFAULT_MODEL_ID: ModelId = "openrouter/free";
|
|
116
|
-
|
|
117
|
-
/** Backward compat alias */
|
|
118
|
-
export const DEFAULT_MODEL: ModelId = DEFAULT_MODEL_ID;
|
|
119
|
-
|
|
120
|
-
/** Models that support extended thinking */
|
|
121
|
-
export const THINKING_MODELS: ReadonlySet<ModelId> = new Set<ModelId>([
|
|
122
|
-
"anthropic/claude-sonnet-4",
|
|
123
|
-
"deepseek/deepseek-r1:free",
|
|
124
|
-
"google/gemini-2.5-flash-preview",
|
|
125
|
-
"ollama/deepseek-r1",
|
|
126
|
-
]);
|
|
127
|
-
|
|
128
|
-
/** Check if a model supports extended thinking */
|
|
129
|
-
export function supportsThinking(model: ModelId): boolean {
|
|
130
|
-
return THINKING_MODELS.has(model);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/** Check if a model is an Ollama model */
|
|
134
|
-
export function isOllamaModel(model: ModelId): boolean {
|
|
135
|
-
return (model as string).startsWith("ollama/");
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/** Get all model IDs for a specific provider prefix */
|
|
139
|
-
export function getModelsByProvider(provider: string): ModelId[] {
|
|
140
|
-
return (Object.keys(MODELS) as ModelId[]).filter((id) =>
|
|
141
|
-
(id as string).startsWith(`${provider}/`)
|
|
142
|
-
);
|
|
143
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Origen types — no runtime deps, safe for client + server.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/** D1-compatible database interface for tool execution */
|
|
6
|
-
export interface D1Like {
|
|
7
|
-
prepare(sql: string): {
|
|
8
|
-
bind(...params: unknown[]): {
|
|
9
|
-
all(): Promise<{ results?: Record<string, unknown>[] }>;
|
|
10
|
-
first(): Promise<Record<string, unknown> | null>;
|
|
11
|
-
run(): Promise<{ meta?: { changes: number; last_row_id: number } }>;
|
|
12
|
-
};
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** Function that provides a D1 instance to tool executors */
|
|
17
|
-
export type D1Provider = () => Promise<D1Like>;
|
|
18
|
-
|
|
19
|
-
/** Chat context passed from the UI (what the user is reading) */
|
|
20
|
-
export interface ReadingContext {
|
|
21
|
-
translation: string;
|
|
22
|
-
bookCode: string;
|
|
23
|
-
chapter: number;
|
|
24
|
-
selectedVerses?: number[];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** Agent message with optional metadata */
|
|
28
|
-
export interface AgentMessage {
|
|
29
|
-
role: "user" | "assistant" | "system";
|
|
30
|
-
content: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** SSE events streamed from the agent to the UI */
|
|
34
|
-
export type StreamEvent =
|
|
35
|
-
| { type: "thinking"; content: string }
|
|
36
|
-
| { type: "tool_call"; name: string; args: Record<string, unknown> }
|
|
37
|
-
| { type: "tool_result"; name: string; result: string }
|
|
38
|
-
| { type: "text"; content: string }
|
|
39
|
-
| { type: "done"; message: string; citations: Citation[]; usage?: UsageInfo }
|
|
40
|
-
| { type: "error"; message: string };
|
|
41
|
-
|
|
42
|
-
export interface Citation {
|
|
43
|
-
book: string;
|
|
44
|
-
chapter: number;
|
|
45
|
-
verse: number;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface UsageInfo {
|
|
49
|
-
promptTokens?: number;
|
|
50
|
-
completionTokens?: number;
|
|
51
|
-
totalCost?: number;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Model configuration entry */
|
|
55
|
-
export interface ModelConfig {
|
|
56
|
-
name: string;
|
|
57
|
-
description: string;
|
|
58
|
-
free: boolean;
|
|
59
|
-
}
|
package/test/origen.test.ts
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Origen v0.3 — unit tests for the core engine.
|
|
3
|
-
* Tests model resolution, Soul.md parsing, message conversion, auth, and tool adapter wiring.
|
|
4
|
-
* No external dependencies — scholar-tools tests live in the scholar monorepo.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { describe, test, expect } from "vitest";
|
|
8
|
-
import { resolveModel } from "../src/adapter";
|
|
9
|
-
import { MODELS, DEFAULT_MODEL, DEFAULT_MODEL_ID, isOllamaModel, getModelsByProvider, getModelsForUI, supportsThinking, THINKING_MODELS, type ModelId } from "../src/models";
|
|
10
|
-
import { Soul, loadSoul } from "../src/soul";
|
|
11
|
-
import { checkOpenRouterAuth, checkAuth } from "../src/agent";
|
|
12
|
-
|
|
13
|
-
// ── Model Resolution ──────────────────────────────────────────────────
|
|
14
|
-
|
|
15
|
-
describe("Model Resolution", () => {
|
|
16
|
-
test("resolves OpenRouter models", () => {
|
|
17
|
-
const model = resolveModel("openrouter/free");
|
|
18
|
-
expect(model.id).toBe("openrouter/free");
|
|
19
|
-
expect(model.provider).toBeDefined();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test("resolves Ollama models with custom base URL", () => {
|
|
23
|
-
const model = resolveModel("ollama/llama3", { ollamaBaseUrl: "http://192.168.1.100:11434/v1" });
|
|
24
|
-
expect(model.id).toBe("llama3");
|
|
25
|
-
expect(model.provider).toBe("ollama");
|
|
26
|
-
expect(model.baseUrl).toBe("http://192.168.1.100:11434/v1");
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test("resolves Ollama models with default base URL", () => {
|
|
30
|
-
const model = resolveModel("ollama/llama3");
|
|
31
|
-
expect(model.baseUrl).toBe("http://localhost:11434/v1");
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
test("resolves unknown Ollama models generically", () => {
|
|
35
|
-
const model = resolveModel("ollama/my-custom-model");
|
|
36
|
-
expect(model.id).toBe("my-custom-model");
|
|
37
|
-
expect(model.name).toContain("Ollama");
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("isOllamaModel correctly identifies Ollama models", () => {
|
|
41
|
-
expect(isOllamaModel("ollama/llama3")).toBe(true);
|
|
42
|
-
expect(isOllamaModel("openrouter/free")).toBe(false);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
// ── MODELS Registry ─────────────────────────────────────────────────
|
|
47
|
-
|
|
48
|
-
describe("MODELS Registry", () => {
|
|
49
|
-
test("has required models", () => {
|
|
50
|
-
expect(MODELS).toHaveProperty("openrouter/free");
|
|
51
|
-
expect(MODELS).toHaveProperty("google/gemma-4-31b-it:free");
|
|
52
|
-
expect(MODELS).toHaveProperty("deepseek/deepseek-r1:free");
|
|
53
|
-
expect(MODELS).toHaveProperty("anthropic/claude-sonnet-4");
|
|
54
|
-
expect(MODELS).toHaveProperty("ollama/llama3");
|
|
55
|
-
expect(MODELS).toHaveProperty("qwen/qwen3-coder:free");
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test("DEFAULT_MODEL aliases match", () => {
|
|
59
|
-
expect(DEFAULT_MODEL_ID).toBe("openrouter/free");
|
|
60
|
-
expect(DEFAULT_MODEL).toBe("openrouter/free");
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test("THINKING_MODELS includes expected entries", () => {
|
|
64
|
-
expect(THINKING_MODELS.has("anthropic/claude-sonnet-4")).toBe(true);
|
|
65
|
-
expect(supportsThinking("deepseek/deepseek-r1:free")).toBe(true);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test("getModelsForUI strips internal fields", () => {
|
|
69
|
-
const uiModels = getModelsForUI();
|
|
70
|
-
for (const config of Object.values(uiModels)) {
|
|
71
|
-
expect(config).toHaveProperty("name");
|
|
72
|
-
expect(config).toHaveProperty("description");
|
|
73
|
-
expect(config).toHaveProperty("free");
|
|
74
|
-
expect((config as any)._model).toBeUndefined();
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test("getModelsByProvider returns correct subsets", () => {
|
|
79
|
-
const orModels = getModelsByProvider("openrouter");
|
|
80
|
-
expect(orModels.length).toBeGreaterThan(0);
|
|
81
|
-
orModels.forEach(id => expect((id as string).startsWith("openrouter/")).toBe(true));
|
|
82
|
-
|
|
83
|
-
const ollamaModels = getModelsByProvider("ollama");
|
|
84
|
-
expect(ollamaModels.length).toBeGreaterThan(0);
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// ── Soul.md ────────────────────────────────────────────────────────
|
|
89
|
-
|
|
90
|
-
describe("Soul.md", () => {
|
|
91
|
-
test("parses basic Soul config from YAML front matter", () => {
|
|
92
|
-
const soulMd = `---
|
|
93
|
-
id: test.soul
|
|
94
|
-
name: TestBot
|
|
95
|
-
version: 1
|
|
96
|
-
identity:
|
|
97
|
-
archetype: assistant
|
|
98
|
-
---
|
|
99
|
-
|
|
100
|
-
# TestBot
|
|
101
|
-
|
|
102
|
-
You are a helpful assistant.
|
|
103
|
-
`;
|
|
104
|
-
const soul = loadSoul(soulMd);
|
|
105
|
-
expect(soul.config.id).toBe("test.soul");
|
|
106
|
-
expect(soul.config.name).toBe("TestBot");
|
|
107
|
-
expect(soul.config.identity?.archetype).toBe("assistant");
|
|
108
|
-
const prompt = soul.buildPrompt();
|
|
109
|
-
expect(prompt).toContain("TestBot");
|
|
110
|
-
expect(prompt).toContain("helpful assistant");
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test("selectProfile returns modified Soul with profile overrides", () => {
|
|
114
|
-
const soulMd = `---
|
|
115
|
-
id: test.soul
|
|
116
|
-
version: 1
|
|
117
|
-
name: TestBot
|
|
118
|
-
identity:
|
|
119
|
-
archetype: assistant
|
|
120
|
-
profiles:
|
|
121
|
-
- default
|
|
122
|
-
- concise
|
|
123
|
-
profile_overrides:
|
|
124
|
-
concise:
|
|
125
|
-
voice:
|
|
126
|
-
verbosity: 20
|
|
127
|
-
instructions: Be extremely concise.
|
|
128
|
-
---
|
|
129
|
-
|
|
130
|
-
# TestBot
|
|
131
|
-
|
|
132
|
-
You are a helpful assistant.
|
|
133
|
-
`;
|
|
134
|
-
const soul = loadSoul(soulMd);
|
|
135
|
-
const concise = soul.selectProfile("concise");
|
|
136
|
-
const prompt = concise.buildPrompt();
|
|
137
|
-
expect(prompt).toContain("extremely concise");
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
// ── Auth Check ─────────────────────────────────────────────────────
|
|
142
|
-
|
|
143
|
-
describe("Auth Check", () => {
|
|
144
|
-
test("checkOpenRouterAuth returns authenticated when key provided", async () => {
|
|
145
|
-
const result = await checkOpenRouterAuth(async () => "sk-test-key");
|
|
146
|
-
expect(result.authenticated).toBe(true);
|
|
147
|
-
expect(result.apiKey).toBe("sk-test-key");
|
|
148
|
-
expect(result.provider).toBe("openrouter");
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
test("checkOpenRouterAuth returns error when no key", async () => {
|
|
152
|
-
const result = await checkOpenRouterAuth(async () => null);
|
|
153
|
-
expect(result.authenticated).toBe(false);
|
|
154
|
-
expect(result.error).toBeDefined();
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
test("checkAuth with Ollama provider returns authenticated", async () => {
|
|
158
|
-
const result = await checkAuth(async (provider) => {
|
|
159
|
-
if (provider === "ollama") return "ollama";
|
|
160
|
-
return undefined;
|
|
161
|
-
});
|
|
162
|
-
expect(result.authenticated).toBe(true);
|
|
163
|
-
expect(result.provider).toBe("ollama");
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
test("checkAuth returns error when no provider has key", async () => {
|
|
167
|
-
const result = await checkAuth(async () => undefined);
|
|
168
|
-
expect(result.authenticated).toBe(false);
|
|
169
|
-
expect(result.error).toBeDefined();
|
|
170
|
-
});
|
|
171
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"strict": true,
|
|
7
|
-
"noEmit": true,
|
|
8
|
-
"declaration": true,
|
|
9
|
-
"declarationMap": true,
|
|
10
|
-
"sourceMap": true,
|
|
11
|
-
"esModuleInterop": true,
|
|
12
|
-
"skipLibCheck": true,
|
|
13
|
-
"outDir": "./dist",
|
|
14
|
-
"rootDir": "./src"
|
|
15
|
-
},
|
|
16
|
-
"include": ["src"]
|
|
17
|
-
}
|