feat(coding-agent): replace model registry with model runtime
Move provider auth and OAuth flows onto pi-ai Models, compose models.json and extension overlays through ModelRuntime, and retain ModelRegistry as an extension compatibility facade.
This commit is contained in:
@@ -46,7 +46,7 @@ import {
|
||||
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
||||
|
||||
// =============================================================================
|
||||
// OAuth Implementation (copied from packages/ai/src/utils/oauth/anthropic.ts)
|
||||
// OAuth implementation adapted for the legacy extension compatibility interface.
|
||||
// =============================================================================
|
||||
|
||||
const decode = (s: string) => atob(s);
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
*/
|
||||
|
||||
import { getModel } from "@earendil-works/pi-ai/compat";
|
||||
import { AuthStorage, createAgentSession, ModelRegistry } from "@earendil-works/pi-coding-agent";
|
||||
import { createAgentSession, ModelRuntime } from "@earendil-works/pi-coding-agent";
|
||||
|
||||
// Set up auth storage and model registry
|
||||
const authStorage = AuthStorage.create();
|
||||
const modelRegistry = ModelRegistry.create(authStorage);
|
||||
const modelRuntime = await ModelRuntime.create();
|
||||
|
||||
// Option 1: Find a specific built-in model by provider/id
|
||||
const opus = getModel("anthropic", "claude-opus-4-5");
|
||||
@@ -18,13 +16,13 @@ if (opus) {
|
||||
}
|
||||
|
||||
// Option 2: Find model via registry (includes custom models from models.json)
|
||||
const customModel = modelRegistry.find("my-provider", "my-model");
|
||||
const customModel = modelRuntime.getModel("my-provider", "my-model");
|
||||
if (customModel) {
|
||||
console.log(`Found custom model: ${customModel.provider}/${customModel.id}`);
|
||||
}
|
||||
|
||||
// Option 3: Pick from available models (have valid API keys)
|
||||
const available = await modelRegistry.getAvailable();
|
||||
const available = await modelRuntime.getAvailable();
|
||||
console.log(
|
||||
"Available models:",
|
||||
available.map((m) => `${m.provider}/${m.id}`),
|
||||
@@ -34,8 +32,7 @@ if (available.length > 0) {
|
||||
const { session } = await createAgentSession({
|
||||
model: available[0],
|
||||
thinkingLevel: "medium", // off, low, medium, high
|
||||
authStorage,
|
||||
modelRegistry,
|
||||
modelRuntime,
|
||||
});
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,52 +1,34 @@
|
||||
/**
|
||||
* API Keys and OAuth
|
||||
*
|
||||
* Configure API key resolution via AuthStorage and ModelRegistry.
|
||||
* Configure provider auth through ModelRuntime.
|
||||
*/
|
||||
|
||||
import { AuthStorage, createAgentSession, ModelRegistry, SessionManager } from "@earendil-works/pi-coding-agent";
|
||||
|
||||
// Default: AuthStorage uses ~/.pi/agent/auth.json
|
||||
// ModelRegistry loads built-in + custom models from ~/.pi/agent/models.json
|
||||
const authStorage = AuthStorage.create();
|
||||
const modelRegistry = ModelRegistry.create(authStorage);
|
||||
import { createAgentSession, ModelRuntime, SessionManager } from "@earendil-works/pi-coding-agent";
|
||||
|
||||
const modelRuntime = await ModelRuntime.create();
|
||||
const { session: defaultAuthSession } = await createAgentSession({
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
authStorage,
|
||||
modelRegistry,
|
||||
modelRuntime,
|
||||
});
|
||||
console.log("Session with default auth storage and model registry");
|
||||
console.log("Session with default model runtime");
|
||||
defaultAuthSession.dispose();
|
||||
|
||||
// Custom auth storage location
|
||||
const customAuthStorage = AuthStorage.create("/tmp/my-app/auth.json");
|
||||
const customModelRegistry = ModelRegistry.create(customAuthStorage, "/tmp/my-app/models.json");
|
||||
|
||||
const customRuntime = await ModelRuntime.create({
|
||||
authPath: "/tmp/my-app/auth.json",
|
||||
modelsPath: "/tmp/my-app/models.json",
|
||||
});
|
||||
const { session: customAuthSession } = await createAgentSession({
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
authStorage: customAuthStorage,
|
||||
modelRegistry: customModelRegistry,
|
||||
modelRuntime: customRuntime,
|
||||
});
|
||||
console.log("Session with custom auth storage location");
|
||||
console.log("Session with custom auth and models locations");
|
||||
customAuthSession.dispose();
|
||||
|
||||
// Runtime API key override (not persisted to disk)
|
||||
authStorage.setRuntimeApiKey("anthropic", "sk-my-temp-key");
|
||||
modelRuntime.setRuntimeApiKey("anthropic", "sk-my-temp-key");
|
||||
const { session: runtimeKeySession } = await createAgentSession({
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
authStorage,
|
||||
modelRegistry,
|
||||
modelRuntime,
|
||||
});
|
||||
console.log("Session with runtime API key override");
|
||||
runtimeKeySession.dispose();
|
||||
|
||||
// No models.json - only built-in models
|
||||
const simpleRegistry = ModelRegistry.inMemory(authStorage);
|
||||
const { session: builtInModelsSession } = await createAgentSession({
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
authStorage,
|
||||
modelRegistry: simpleRegistry,
|
||||
});
|
||||
console.log("Session with only built-in models");
|
||||
builtInModelsSession.dispose();
|
||||
|
||||
@@ -6,26 +6,22 @@
|
||||
|
||||
import { getModel } from "@earendil-works/pi-ai/compat";
|
||||
import {
|
||||
AuthStorage,
|
||||
createAgentSession,
|
||||
createExtensionRuntime,
|
||||
ModelRegistry,
|
||||
ModelRuntime,
|
||||
type ResourceLoader,
|
||||
SessionManager,
|
||||
SettingsManager,
|
||||
} from "@earendil-works/pi-coding-agent";
|
||||
|
||||
// Custom auth storage location
|
||||
const authStorage = AuthStorage.create("/tmp/my-agent/auth.json");
|
||||
|
||||
// Runtime API key override (not persisted)
|
||||
const modelRuntime = await ModelRuntime.create({
|
||||
authPath: "/tmp/my-agent/auth.json",
|
||||
modelsPath: "/tmp/my-agent/models.json",
|
||||
});
|
||||
if (process.env.MY_ANTHROPIC_KEY) {
|
||||
authStorage.setRuntimeApiKey("anthropic", process.env.MY_ANTHROPIC_KEY);
|
||||
modelRuntime.setRuntimeApiKey("anthropic", process.env.MY_ANTHROPIC_KEY);
|
||||
}
|
||||
|
||||
// Model registry with no custom models.json
|
||||
const modelRegistry = ModelRegistry.inMemory(authStorage);
|
||||
|
||||
const model = getModel("anthropic", "claude-sonnet-4-5");
|
||||
if (!model) throw new Error("Model not found");
|
||||
|
||||
@@ -55,8 +51,7 @@ const { session } = await createAgentSession({
|
||||
agentDir: "/tmp/my-agent",
|
||||
model,
|
||||
thinkingLevel: "off",
|
||||
authStorage,
|
||||
modelRegistry,
|
||||
modelRuntime,
|
||||
resourceLoader,
|
||||
tools: ["read", "bash"],
|
||||
sessionManager: SessionManager.inMemory(cwd),
|
||||
|
||||
@@ -34,46 +34,44 @@ npx tsx examples/sdk/01-minimal.ts
|
||||
```typescript
|
||||
import { getModel } from "@earendil-works/pi-ai";
|
||||
import {
|
||||
AuthStorage,
|
||||
createAgentSession,
|
||||
DefaultResourceLoader,
|
||||
ModelRegistry,
|
||||
ModelRuntime,
|
||||
SessionManager,
|
||||
SettingsManager,
|
||||
} from "@earendil-works/pi-coding-agent";
|
||||
|
||||
// Auth and models setup
|
||||
const authStorage = AuthStorage.create();
|
||||
const modelRegistry = ModelRegistry.create(authStorage);
|
||||
const modelRuntime = await ModelRuntime.create();
|
||||
|
||||
// Minimal
|
||||
const { session } = await createAgentSession({ authStorage, modelRegistry });
|
||||
const { session } = await createAgentSession({ modelRuntime });
|
||||
|
||||
// Custom model
|
||||
const model = getModel("anthropic", "claude-opus-4-5");
|
||||
const { session } = await createAgentSession({ model, thinkingLevel: "high", authStorage, modelRegistry });
|
||||
const { session } = await createAgentSession({ model, thinkingLevel: "high", modelRuntime });
|
||||
|
||||
// Modify prompt
|
||||
const loader = new DefaultResourceLoader({
|
||||
systemPromptOverride: (base) => `${base}\n\nBe concise.`,
|
||||
});
|
||||
await loader.reload();
|
||||
const { session } = await createAgentSession({ resourceLoader: loader, authStorage, modelRegistry });
|
||||
const { session } = await createAgentSession({ resourceLoader: loader, modelRuntime });
|
||||
|
||||
// Read-only
|
||||
const { session } = await createAgentSession({ tools: ["read", "grep", "find", "ls"], authStorage, modelRegistry });
|
||||
const { session } = await createAgentSession({ tools: ["read", "grep", "find", "ls"], modelRuntime });
|
||||
|
||||
// In-memory
|
||||
const { session } = await createAgentSession({
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
authStorage,
|
||||
modelRegistry,
|
||||
modelRuntime,
|
||||
});
|
||||
|
||||
// Full control
|
||||
const customAuth = AuthStorage.create("/my/app/auth.json");
|
||||
customAuth.setRuntimeApiKey("anthropic", process.env.MY_KEY!);
|
||||
const customRegistry = ModelRegistry.create(customAuth);
|
||||
const customRuntime = await ModelRuntime.create({
|
||||
authPath: "/my/app/auth.json",
|
||||
modelsPath: "/my/app/models.json",
|
||||
});
|
||||
customRuntime.setRuntimeApiKey("anthropic", process.env.MY_KEY!);
|
||||
|
||||
const resourceLoader = new DefaultResourceLoader({
|
||||
systemPromptOverride: () => "You are helpful.",
|
||||
@@ -86,8 +84,7 @@ await resourceLoader.reload();
|
||||
|
||||
const { session } = await createAgentSession({
|
||||
model,
|
||||
authStorage: customAuth,
|
||||
modelRegistry: customRegistry,
|
||||
modelRuntime: customRuntime,
|
||||
resourceLoader,
|
||||
tools: ["read", "bash", "my_tool"],
|
||||
customTools: [myTool],
|
||||
@@ -108,8 +105,7 @@ await session.prompt("Hello");
|
||||
|
||||
| Option | Default | Description |
|
||||
|--------|---------|-------------|
|
||||
| `authStorage` | `AuthStorage.create()` | Credential storage |
|
||||
| `modelRegistry` | `ModelRegistry.create(authStorage)` | Model registry |
|
||||
| `modelRuntime` | Runtime using `agentDir/auth.json` and `models.json` | Canonical model and authentication runtime |
|
||||
| `cwd` | `process.cwd()` | Working directory |
|
||||
| `agentDir` | `~/.pi/agent` | Config directory |
|
||||
| `model` | From settings/first available | Model to use |
|
||||
|
||||
Reference in New Issue
Block a user