@odla-ai/ai 0.1.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/llms.txt ADDED
@@ -0,0 +1,111 @@
1
+ # @odla-ai/ai — LLM context
2
+
3
+ One general-purpose interface for AI inference across Anthropic (Claude), OpenAI
4
+ (GPT), and Google (Gemini) — text, image, and audio — plus a tool-use agent
5
+ engine and an eval harness. Library-only: import in-process, bring your own keys.
6
+ Isomorphic (Node 20+, Cloudflare Workers, browser). Provider SDKs are lazy-loaded.
7
+
8
+ ## Install
9
+
10
+ npm i @odla-ai/ai
11
+ # provider SDKs are peer-lazy-loaded; install those you use:
12
+ # @anthropic-ai/sdk openai @google/genai
13
+ # optional, for odla-db-backed storage + secrets:
14
+ # @odla-ai/db
15
+
16
+ ## Core call
17
+
18
+ import { init } from "@odla-ai/ai";
19
+ const ai = init({ keys: { anthropic: KEY, openai: KEY, google: KEY }, defaultModel: "claude-opus-4-8" });
20
+ const res = await ai.chat({ model: "gpt-5", messages: [{ role: "user", content: "hi" }], maxTokens: 256 });
21
+ // res.content: OracleContentBlock[] (text/tool_use/thinking)
22
+
23
+ - `ai.stream(input)` → async iterable of normalized events: message_start →
24
+ content_block_start → content_block_delta {text_delta|thinking_delta|input_json_delta}
25
+ → content_block_stop → message_delta → message_stop (plus `error`).
26
+ - `ai.extract<T>({ model, user, tool })` → forced tool call; returns a parsed object.
27
+ - `ai.search({ model, user })` → web-search-grounded prose.
28
+
29
+ ## Content blocks (multimodal)
30
+
31
+ text; image { source: base64|url }; audio { source: base64|url }; document (PDF);
32
+ tool_use { id, name, input }; tool_result { toolUseId, content }; thinking.
33
+ Audio input is accepted by OpenAI (`gpt-audio`) and Google, NOT by Claude — an
34
+ audio block to a Claude model throws CapabilityError before any network call.
35
+
36
+ ## Agents
37
+
38
+ import { runAgent, type Persona, type ToolDef } from "@odla-ai/ai";
39
+ const add: ToolDef = { name: "add", description: "add", inputSchema: {...}, handler: (i) => ({ content: String(i.a + i.b) }) };
40
+ const persona: Persona = { name: "calc", model: "claude-opus-4-8", system: "...", tools: [add], maxSteps: 4 };
41
+ const run = await runAgent(ai, persona, { input: "what is 21+21?" });
42
+ // run.finalText, run.toolCalls, run.usage, run.steps, run.stoppedReason
43
+
44
+ Tools may carry taint labels (outputTaint / acceptsTaint) for a CaMeL-style
45
+ prompt-injection gate. Personas may carry a MemoryScope.
46
+
47
+ ## Skills
48
+
49
+ A Skill = { name, instructions?, tools: ToolDef[] } is a bundle you attach to a
50
+ persona (persona.skills); its tools merge into the model's tool list and its
51
+ instructions into the system prompt. Turn an odla-db entity into CRUD tools:
52
+
53
+ import { entityCrudSkill } from "@odla-ai/ai";
54
+ const todos = entityCrudSkill({ db, entity: "todos", fields: { text: { type: "string" }, done: { type: "boolean" } }, required: ["text"] });
55
+ // → tools: list_todos, create_todo, update_todo (partial), delete_todo — handlers write to odla-db
56
+ const run = await runAgent(ai, { name: "todo-bot", model: "gpt-5", skills: [todos] }, { input: "add buy milk" });
57
+
58
+ ## Evals
59
+
60
+ import { evaluate, exactMatch, llmJudge } from "@odla-ai/ai";
61
+ const report = await evaluate({ inference: ai, model: "claude-opus-4-8", grader: exactMatch(), cases: [{ input, expected }] });
62
+ // graders: exactMatch, includes, structuredMatch, llmJudge; pass a persona to eval an agent.
63
+
64
+ ## Storage + BYO keys via odla-db (@odla-ai/db) — the handshake
65
+
66
+ Follows odla-db's model. An agent never takes a platform secret; it does a
67
+ device-authorization handshake for a scoped, revocable token, then provisions.
68
+
69
+ import { requestToken, init as odlaInit } from "@odla-ai/db";
70
+ import { provisionAgentApp, odlaDbKeyResolver, OdlaDbMemory, persistRun, init } from "@odla-ai/ai";
71
+
72
+ // 1. Handshake: the human supplies the endpoint and approves a short code in odla-db Studio.
73
+ const { token } = await requestToken({ endpoint: ODLA_URL, onCode: ({ userCode }) => show(userCode) });
74
+
75
+ // 2. Provision: create app, mint app key, push agent schema, store BYO provider keys as secrets.
76
+ const { appId, appKey } = await provisionAgentApp({ endpoint: ODLA_URL, token, providerKeys: { openai: OPENAI_KEY } });
77
+
78
+ // 3. Runtime: read keys from odla-db secrets; persist memory + run traces.
79
+ const db = odlaInit({ appId, adminToken: appKey, endpoint: ODLA_URL });
80
+ const ai = init({ resolveKey: odlaDbKeyResolver(db) });
81
+ const persona = { name: "assistant", model: "gpt-5", memory: new OdlaDbMemory({ db, sessionId: "user-42" }) };
82
+ const run = await runAgent(ai, persona, { input: "hello" });
83
+ await persistRun(run, { db, sessionId: "user-42" });
84
+
85
+ Secrets are read-only from the app key (odlaDbKeyResolver → db.secrets.get); they
86
+ are WRITTEN only with the operator/dev token via provisionAgentApp/putSecret. Keys
87
+ are AES-GCM-encrypted at rest in odla-db and never returned to browser clients.
88
+
89
+ ## Errors
90
+
91
+ Every error is an OdlaAIError subclass with a stable `code` — branch on err.code:
92
+ config, auth, rate_limit, capability_unsupported, context_window, invalid_request,
93
+ tool_input_invalid, provider_error.
94
+
95
+ ## Models
96
+
97
+ - `claude-opus-4-8` (anthropic) — text, image, tools, thinking, effort, web-search, structured
98
+ - `claude-sonnet-5` (anthropic) — text, image, tools, thinking, effort, web-search, structured
99
+ - `claude-fable-5` (anthropic) — text, image, tools, thinking, effort, web-search, structured
100
+ - `claude-haiku-4-5` (anthropic) — text, image, tools, structured
101
+ - `gpt-5.5` (openai) — text, image, tools, effort, structured
102
+ - `gpt-5` (openai) — text, image, tools, effort, structured
103
+ - `gpt-5-mini` (openai) — text, image, tools, effort, structured
104
+ - `gpt-4o` (openai) — text, image, tools, structured
105
+ - `gpt-audio` (openai) — text, audio, tools, structured
106
+ - `gemini-2.5-pro` (google) — text, image, audio, tools, thinking, effort, web-search, structured
107
+ - `gemini-2.5-flash` (google) — text, image, audio, tools, thinking, effort, web-search, structured
108
+ - `gemini-2.0-flash` (google) — text, image, audio, tools, web-search, structured
109
+
110
+ Model ids live in a data catalog and drift — verify against provider docs; extend
111
+ via buildCatalog / init({ catalog }).
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "@odla-ai/ai",
3
+ "version": "0.1.0",
4
+ "description": "One general-purpose interface for AI inference across Anthropic (Claude), OpenAI, and Google (Gemini) — text, image, and audio — plus a tool-use agent engine and eval harness. The place every odla app reaches for when it needs AI. Isomorphic: Node 20+, Cloudflare Workers, the browser.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "llms.txt"
21
+ ],
22
+ "sideEffects": false,
23
+ "engines": {
24
+ "node": ">=20"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "keywords": [
30
+ "odla",
31
+ "odla-ai",
32
+ "ai",
33
+ "llm",
34
+ "claude",
35
+ "anthropic",
36
+ "openai",
37
+ "gpt",
38
+ "google",
39
+ "gemini",
40
+ "agent",
41
+ "inference",
42
+ "multimodal",
43
+ "tool-use"
44
+ ],
45
+ "scripts": {
46
+ "build": "tsup",
47
+ "clean": "rm -rf dist",
48
+ "typecheck": "tsc --noEmit",
49
+ "test": "vitest run",
50
+ "smoke": "tsx scripts/smoke.ts",
51
+ "gen:llms": "tsx -e \"import('./src/index.ts').then(m => process.stdout.write(m.generateLlmsTxt()))\" > llms.txt",
52
+ "check:publish": "tsx scripts/check-publish-safety.ts",
53
+ "prepublishOnly": "npm run build && npm run check:publish"
54
+ },
55
+ "dependencies": {
56
+ "@anthropic-ai/sdk": "^0.70.0",
57
+ "@google/genai": "^1.0.0",
58
+ "openai": "^6.0.0"
59
+ },
60
+ "peerDependencies": {
61
+ "@odla-ai/db": "^0.2.0"
62
+ },
63
+ "peerDependenciesMeta": {
64
+ "@odla-ai/db": {
65
+ "optional": true
66
+ }
67
+ },
68
+ "devDependencies": {
69
+ "@odla-ai/db": "^0.2.1",
70
+ "@types/node": "^22.10.0",
71
+ "tsup": "^8.3.0",
72
+ "tsx": "^4.19.0",
73
+ "typescript": "^5.7.2",
74
+ "vitest": "^3.0.0"
75
+ }
76
+ }