426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
736 lines
17 KiB
Plaintext
736 lines
17 KiB
Plaintext
---
|
|
title: "Model Management"
|
|
description: "One API for any AI model - OpenAI, Anthropic, Ollama, and more"
|
|
---
|
|
|
|
## Why a Model Layer?
|
|
|
|
Switching between OpenAI, Anthropic, or local models shouldn't require rewriting your agent. The model layer gives you:
|
|
|
|
- **One API** for all providers
|
|
- **Automatic fallback** when a provider fails
|
|
- **Streaming** with consistent callbacks
|
|
- **Type-safe** parameters per model type
|
|
|
|
```typescript
|
|
// Same code works with any provider
|
|
const response = await runtime.useModel(ModelType.TEXT_LARGE, {
|
|
prompt: "Explain quantum computing",
|
|
temperature: 0.7,
|
|
});
|
|
```
|
|
|
|
<Tip>
|
|
**Provider is configured, not coded.** Set `OPENAI_API_KEY` or
|
|
`ANTHROPIC_API_KEY` - your code doesn't change.
|
|
</Tip>
|
|
|
|
## Model System
|
|
|
|
The model management system provides a standard interface for AI model access with automatic provider selection, priority-based routing, and type-safe parameters.
|
|
|
|
## Model Types
|
|
|
|
### Core Model Types
|
|
|
|
```typescript
|
|
enum ModelType {
|
|
// Text generation models
|
|
TEXT_SMALL = "text:small", // Fast, simple responses
|
|
TEXT_MEDIUM = "text:medium", // Balanced performance
|
|
TEXT_LARGE = "text:large", // Complex reasoning
|
|
|
|
// Embedding models
|
|
TEXT_EMBEDDING = "text:embedding",
|
|
|
|
// Image models
|
|
IMAGE_GENERATION = "image:generation",
|
|
IMAGE_ANALYSIS = "image:analysis",
|
|
|
|
// Audio models
|
|
SPEECH_TO_TEXT = "speech:to:text",
|
|
TEXT_TO_SPEECH = "text:to:speech",
|
|
|
|
// Specialized models
|
|
CODE_GENERATION = "code:generation",
|
|
CLASSIFICATION = "classification",
|
|
}
|
|
```
|
|
|
|
### Model Parameters
|
|
|
|
Type-safe parameters for each model type:
|
|
|
|
```typescript
|
|
// Text generation parameters
|
|
interface TextGenerationParams {
|
|
prompt: string;
|
|
messages?: Message[];
|
|
temperature?: number; // 0.0 - 2.0
|
|
maxTokens?: number;
|
|
topP?: number;
|
|
frequencyPenalty?: number;
|
|
presencePenalty?: number;
|
|
stopSequences?: string[];
|
|
systemPrompt?: string;
|
|
}
|
|
|
|
// Embedding parameters
|
|
interface EmbeddingParams {
|
|
input: string | string[];
|
|
model?: string;
|
|
dimensions?: number;
|
|
}
|
|
|
|
// Image generation parameters
|
|
interface ImageGenerationParams {
|
|
prompt: string;
|
|
negativePrompt?: string;
|
|
width?: number;
|
|
height?: number;
|
|
steps?: number;
|
|
seed?: number;
|
|
style?: string;
|
|
}
|
|
|
|
// Speech-to-text parameters
|
|
interface SpeechToTextParams {
|
|
audio: Buffer | string; // Audio data or URL
|
|
language?: string;
|
|
format?: "json" | "text" | "srt";
|
|
temperature?: number;
|
|
}
|
|
```
|
|
|
|
## Model Registration
|
|
|
|
### Registering Model Handlers
|
|
|
|
```typescript
|
|
// Register a model handler
|
|
runtime.registerModel(
|
|
ModelType.TEXT_LARGE,
|
|
async (runtime, params) => {
|
|
// Model implementation
|
|
const response = await callAPI(params);
|
|
return response.text;
|
|
},
|
|
"openai", // provider name
|
|
100, // priority (higher = preferred)
|
|
);
|
|
|
|
// Register multiple models from a plugin
|
|
const modelPlugin: Plugin = {
|
|
name: "openai-models",
|
|
|
|
models: [
|
|
{
|
|
type: ModelType.TEXT_LARGE,
|
|
handler: handleTextGeneration,
|
|
provider: "openai",
|
|
priority: 100,
|
|
},
|
|
{
|
|
type: ModelType.TEXT_EMBEDDING,
|
|
handler: handleEmbedding,
|
|
provider: "openai",
|
|
priority: 100,
|
|
},
|
|
],
|
|
};
|
|
```
|
|
|
|
### Model Handler Interface
|
|
|
|
```typescript
|
|
type ModelHandler<T = ModelParams, R = ModelResponse> = (
|
|
runtime: IAgentRuntime,
|
|
params: T,
|
|
) => Promise<R>;
|
|
|
|
interface ModelRegistration {
|
|
type: ModelTypeName;
|
|
handler: ModelHandler;
|
|
provider: string;
|
|
priority: number;
|
|
}
|
|
```
|
|
|
|
## Using Models
|
|
|
|
### Type-Safe Model Usage
|
|
|
|
```typescript
|
|
// Text generation
|
|
const response = await runtime.useModel(ModelType.TEXT_LARGE, {
|
|
prompt: "Explain quantum computing",
|
|
temperature: 0.7,
|
|
maxTokens: 500,
|
|
});
|
|
|
|
// Get embeddings
|
|
const embedding = await runtime.useModel(ModelType.TEXT_EMBEDDING, {
|
|
input: "Text to embed",
|
|
});
|
|
|
|
// Generate image
|
|
const image = await runtime.useModel(ModelType.IMAGE_GENERATION, {
|
|
prompt: "A sunset over mountains",
|
|
width: 1024,
|
|
height: 1024,
|
|
steps: 50,
|
|
});
|
|
|
|
// Speech to text
|
|
const transcript = await runtime.useModel(ModelType.SPEECH_TO_TEXT, {
|
|
audio: audioBuffer,
|
|
language: "en",
|
|
format: "json",
|
|
});
|
|
```
|
|
|
|
### Specifying Provider
|
|
|
|
```typescript
|
|
// Use specific provider
|
|
const response = await runtime.useModel(
|
|
ModelType.TEXT_LARGE,
|
|
{ prompt: "Hello" },
|
|
"anthropic", // Force specific provider
|
|
);
|
|
|
|
// Get available providers
|
|
const providers = runtime.getModelProviders(ModelType.TEXT_LARGE);
|
|
console.log("Available providers:", providers);
|
|
// ['openai', 'anthropic', 'ollama']
|
|
```
|
|
|
|
## Provider Priority
|
|
|
|
### Priority System
|
|
|
|
The runtime selects providers based on priority:
|
|
|
|
```typescript
|
|
// Higher priority providers are preferred
|
|
runtime.registerModel(ModelType.TEXT_LARGE, handlerA, "provider-a", 100);
|
|
runtime.registerModel(ModelType.TEXT_LARGE, handlerB, "provider-b", 90);
|
|
runtime.registerModel(ModelType.TEXT_LARGE, handlerC, "provider-c", 80);
|
|
|
|
// Will use provider-a (priority 100)
|
|
await runtime.useModel(ModelType.TEXT_LARGE, params);
|
|
```
|
|
|
|
### Fallback Mechanism
|
|
|
|
```typescript
|
|
// Automatic fallback on failure
|
|
class ModelRouter {
|
|
async useModel(type: ModelType, params: ModelParams, preferredProvider?: string) {
|
|
const providers = this.getProvidersByPriority(type, preferredProvider);
|
|
|
|
for (const provider of providers) {
|
|
try {
|
|
return await provider.handler(this.runtime, params);
|
|
} catch (error) {
|
|
this.logger.warn(`Provider ${provider.name} failed:`, error);
|
|
|
|
// Try next provider
|
|
if (provider !== providers[providers.length - 1]) {
|
|
continue;
|
|
}
|
|
|
|
// All providers failed
|
|
throw new Error(`No providers available for ${type}`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Model Providers
|
|
|
|
### OpenAI Provider
|
|
|
|
```typescript
|
|
class OpenAIModelProvider {
|
|
private client: OpenAI;
|
|
|
|
constructor(runtime: IAgentRuntime) {
|
|
const apiKey = runtime.getSetting("OPENAI_API_KEY");
|
|
this.client = new OpenAI({ apiKey });
|
|
}
|
|
|
|
async handleTextGeneration(params: TextGenerationParams) {
|
|
const response = await this.client.chat.completions.create({
|
|
model: params.model || "gpt-5",
|
|
messages: params.messages || [{ role: "user", content: params.prompt }],
|
|
temperature: params.temperature,
|
|
max_tokens: params.maxTokens,
|
|
top_p: params.topP,
|
|
frequency_penalty: params.frequencyPenalty,
|
|
presence_penalty: params.presencePenalty,
|
|
stop: params.stopSequences,
|
|
});
|
|
|
|
return response.choices[0].message.content;
|
|
}
|
|
|
|
async handleEmbedding(params: EmbeddingParams) {
|
|
const response = await this.client.embeddings.create({
|
|
model: "text-embedding-3-small",
|
|
input: params.input,
|
|
dimensions: params.dimensions,
|
|
});
|
|
|
|
return Array.isArray(params.input)
|
|
? response.data.map((d) => d.embedding)
|
|
: response.data[0].embedding;
|
|
}
|
|
|
|
register(runtime: IAgentRuntime) {
|
|
runtime.registerModel(
|
|
ModelType.TEXT_LARGE,
|
|
this.handleTextGeneration.bind(this),
|
|
"openai",
|
|
100,
|
|
);
|
|
|
|
runtime.registerModel(
|
|
ModelType.TEXT_EMBEDDING,
|
|
this.handleEmbedding.bind(this),
|
|
"openai",
|
|
100,
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Anthropic Provider
|
|
|
|
```typescript
|
|
class AnthropicModelProvider {
|
|
private client: Anthropic;
|
|
|
|
constructor(runtime: IAgentRuntime) {
|
|
const apiKey = runtime.getSetting("ANTHROPIC_API_KEY");
|
|
this.client = new Anthropic({ apiKey });
|
|
}
|
|
|
|
async handleTextGeneration(params: TextGenerationParams) {
|
|
const response = await this.client.messages.create({
|
|
model: params.model || "claude-opus-4-7",
|
|
messages: params.messages || [{ role: "user", content: params.prompt }],
|
|
max_tokens: params.maxTokens || 1000,
|
|
temperature: params.temperature,
|
|
system: params.systemPrompt,
|
|
});
|
|
|
|
return response.content[0].text;
|
|
}
|
|
|
|
register(runtime: IAgentRuntime) {
|
|
runtime.registerModel(
|
|
ModelType.TEXT_LARGE,
|
|
this.handleTextGeneration.bind(this),
|
|
"anthropic",
|
|
95, // Slightly lower priority than OpenAI
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Local Model Provider (Ollama)
|
|
|
|
```typescript
|
|
class OllamaModelProvider {
|
|
private baseUrl: string;
|
|
|
|
constructor(runtime: IAgentRuntime) {
|
|
this.baseUrl =
|
|
runtime.getSetting("OLLAMA_BASE_URL") || "http://localhost:11434";
|
|
}
|
|
|
|
async handleTextGeneration(params: TextGenerationParams) {
|
|
const response = await fetch(`${this.baseUrl}/api/generate`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
model: params.model || "llama2",
|
|
prompt: params.prompt,
|
|
temperature: params.temperature,
|
|
options: {
|
|
num_predict: params.maxTokens,
|
|
top_p: params.topP,
|
|
stop: params.stopSequences,
|
|
},
|
|
}),
|
|
});
|
|
|
|
const data = await response.json();
|
|
return data.response;
|
|
}
|
|
|
|
async handleEmbedding(params: EmbeddingParams) {
|
|
const response = await fetch(`${this.baseUrl}/api/embeddings`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
model: params.model || "all-minilm",
|
|
prompt: params.input,
|
|
}),
|
|
});
|
|
|
|
const data = await response.json();
|
|
return data.embedding;
|
|
}
|
|
|
|
register(runtime: IAgentRuntime) {
|
|
// Lower priority for local models
|
|
runtime.registerModel(
|
|
ModelType.TEXT_LARGE,
|
|
this.handleTextGeneration.bind(this),
|
|
"ollama",
|
|
50,
|
|
);
|
|
|
|
runtime.registerModel(
|
|
ModelType.TEXT_EMBEDDING,
|
|
this.handleEmbedding.bind(this),
|
|
"ollama",
|
|
50,
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
## Model Selection Strategy
|
|
|
|
### Automatic Selection
|
|
|
|
```typescript
|
|
// Runtime automatically selects best available provider
|
|
const response = await runtime.useModel(ModelType.TEXT_LARGE, {
|
|
prompt: "Hello",
|
|
});
|
|
|
|
// Selection order:
|
|
// 1. Check if preferred provider specified
|
|
// 2. Sort available providers by priority
|
|
// 3. Try each provider until success
|
|
// 4. Cache successful provider for session
|
|
```
|
|
|
|
### Context-Based Selection
|
|
|
|
```typescript
|
|
// Select model based on context
|
|
async function selectModelForTask(runtime: IAgentRuntime, task: string) {
|
|
const complexity = analyzeComplexity(task);
|
|
|
|
if (complexity < 0.3) {
|
|
// Simple task - use small model
|
|
return runtime.useModel(ModelType.TEXT_SMALL, {
|
|
prompt: task,
|
|
temperature: 0.3,
|
|
});
|
|
} else if (complexity < 0.7) {
|
|
// Medium complexity - use medium model
|
|
return runtime.useModel(ModelType.TEXT_MEDIUM, {
|
|
prompt: task,
|
|
temperature: 0.5,
|
|
});
|
|
} else {
|
|
// Complex task - use large model
|
|
return runtime.useModel(ModelType.TEXT_LARGE, {
|
|
prompt: task,
|
|
temperature: 0.7,
|
|
maxTokens: 2000,
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
### Cost Optimization
|
|
|
|
```typescript
|
|
// Track and optimize model usage costs
|
|
class CostOptimizedModelRouter {
|
|
private costs = {
|
|
openai: {
|
|
[ModelType.TEXT_LARGE]: 0.03,
|
|
[ModelType.TEXT_EMBEDDING]: 0.0001,
|
|
},
|
|
anthropic: { [ModelType.TEXT_LARGE]: 0.025 },
|
|
ollama: { [ModelType.TEXT_LARGE]: 0, [ModelType.TEXT_EMBEDDING]: 0 },
|
|
};
|
|
|
|
async useModel(type: ModelType, params: ModelParams, maxCost?: number) {
|
|
const providers = this.getProvidersByCost(type, maxCost);
|
|
|
|
for (const provider of providers) {
|
|
try {
|
|
const result = await provider.handler(this.runtime, params);
|
|
|
|
// Track usage
|
|
this.trackUsage(provider.name, type, params);
|
|
|
|
return result;
|
|
} catch (error) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
private getProvidersByCost(type: ModelType, maxCost?: number) {
|
|
return this.providers
|
|
.filter((p) => {
|
|
const cost = this.costs[p.name]?.[type] || Infinity;
|
|
return !maxCost || cost <= maxCost;
|
|
})
|
|
.sort((a, b) => {
|
|
const costA = this.costs[a.name]?.[type] || Infinity;
|
|
const costB = this.costs[b.name]?.[type] || Infinity;
|
|
return costA - costB;
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
## Model Caching
|
|
|
|
### Response Caching
|
|
|
|
```typescript
|
|
class ModelCache {
|
|
private cache = new Map<string, { result: ModelResponse; timestamp: number }>();
|
|
private ttl = 60 * 60 * 1000; // 1 hour
|
|
|
|
getCacheKey(type: ModelType, params: ModelParams): string {
|
|
return `${type}:${JSON.stringify(params)}`;
|
|
}
|
|
|
|
get(type: ModelType, params: ModelParams): ModelResponse | null {
|
|
const key = this.getCacheKey(type, params);
|
|
const cached = this.cache.get(key);
|
|
|
|
if (!cached) return null;
|
|
|
|
if (Date.now() - cached.timestamp > this.ttl) {
|
|
this.cache.delete(key);
|
|
return null;
|
|
}
|
|
|
|
return cached.result;
|
|
}
|
|
|
|
set(type: ModelType, params: ModelParams, result: ModelResponse) {
|
|
const key = this.getCacheKey(type, params);
|
|
this.cache.set(key, {
|
|
result,
|
|
timestamp: Date.now(),
|
|
});
|
|
}
|
|
}
|
|
|
|
// Use with runtime
|
|
const cache = new ModelCache();
|
|
|
|
async function cachedModelCall(
|
|
runtime: IAgentRuntime,
|
|
type: ModelType,
|
|
params: ModelParams,
|
|
) {
|
|
// Check cache
|
|
const cached = cache.get(type, params);
|
|
if (cached) return cached;
|
|
|
|
// Make call
|
|
const result = await runtime.useModel(type, params);
|
|
|
|
// Cache result
|
|
cache.set(type, params, result);
|
|
|
|
return result;
|
|
}
|
|
```
|
|
|
|
## Model Monitoring
|
|
|
|
### Usage Tracking
|
|
|
|
```typescript
|
|
interface ModelUsageMetrics {
|
|
provider: string;
|
|
modelType: ModelType;
|
|
count: number;
|
|
totalTokens: number;
|
|
totalDuration: number;
|
|
avgDuration: number;
|
|
errors: number;
|
|
cost: number;
|
|
}
|
|
|
|
class ModelMonitor {
|
|
private metrics = new Map<string, ModelUsageMetrics>();
|
|
|
|
async trackUsage(
|
|
provider: string,
|
|
type: ModelType,
|
|
params: ModelParams,
|
|
result: ModelResponse,
|
|
duration: number,
|
|
) {
|
|
const key = `${provider}:${type}`;
|
|
|
|
if (!this.metrics.has(key)) {
|
|
this.metrics.set(key, {
|
|
provider,
|
|
modelType: type,
|
|
count: 0,
|
|
totalTokens: 0,
|
|
totalDuration: 0,
|
|
avgDuration: 0,
|
|
errors: 0,
|
|
cost: 0,
|
|
});
|
|
}
|
|
|
|
const metrics = this.metrics.get(key);
|
|
metrics.count++;
|
|
metrics.totalDuration += duration;
|
|
metrics.avgDuration = metrics.totalDuration / metrics.count;
|
|
|
|
// Estimate tokens (simplified)
|
|
if (type === ModelType.TEXT_LARGE) {
|
|
const tokens =
|
|
this.estimateTokens(params.prompt) + this.estimateTokens(result);
|
|
metrics.totalTokens += tokens;
|
|
metrics.cost += this.calculateCost(provider, type, tokens);
|
|
}
|
|
|
|
// Emit metrics event
|
|
await this.runtime.emit(EventType.MODEL_USED, {
|
|
runtime: this.runtime,
|
|
modelType: type,
|
|
provider,
|
|
params,
|
|
result,
|
|
duration,
|
|
metrics,
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
### Retry Logic
|
|
|
|
```typescript
|
|
async function modelCallWithRetry(
|
|
runtime: IAgentRuntime,
|
|
type: ModelType,
|
|
params: ModelParams,
|
|
maxRetries = 3,
|
|
) {
|
|
let lastError: Error;
|
|
|
|
for (let i = 0; i < maxRetries; i++) {
|
|
try {
|
|
return await runtime.useModel(type, params);
|
|
} catch (error) {
|
|
lastError = error;
|
|
|
|
// Check if retryable
|
|
if (isRateLimitError(error)) {
|
|
// Wait with exponential backoff
|
|
const delay = Math.pow(2, i) * 1000;
|
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
continue;
|
|
}
|
|
|
|
// Non-retryable error
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
throw lastError;
|
|
}
|
|
```
|
|
|
|
### Graceful Degradation
|
|
|
|
```typescript
|
|
// Fallback to simpler models on failure
|
|
async function modelCallWithFallback(
|
|
runtime: IAgentRuntime,
|
|
params: TextGenerationParams,
|
|
) {
|
|
const modelHierarchy = [
|
|
ModelType.TEXT_LARGE,
|
|
ModelType.TEXT_MEDIUM,
|
|
ModelType.TEXT_SMALL,
|
|
];
|
|
|
|
for (const modelType of modelHierarchy) {
|
|
try {
|
|
return await runtime.useModel(modelType, params);
|
|
} catch (error) {
|
|
runtime.logger.warn(`Model ${modelType} failed, trying fallback`);
|
|
|
|
if (modelType === ModelType.TEXT_SMALL) {
|
|
// Last option failed
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### Model Selection
|
|
|
|
- **Right-Size Models**: Use appropriate model size for task complexity
|
|
- **Cost Awareness**: Consider cost when selecting providers
|
|
- **Latency Requirements**: Use local models for low-latency needs
|
|
- **Fallback Strategy**: Implement fallbacks for reliability
|
|
- **Caching**: Cache responses for repeated queries
|
|
|
|
### Performance
|
|
|
|
- **Batch Processing**: Batch multiple requests when possible
|
|
- **Streaming**: Use streaming for long responses
|
|
- **Timeout Handling**: Set appropriate timeouts
|
|
- **Connection Pooling**: Reuse HTTP connections
|
|
- **Rate Limiting**: Respect provider rate limits
|
|
|
|
### Monitoring
|
|
|
|
- **Track Usage**: Monitor token usage and costs
|
|
- **Error Rates**: Track provider error rates
|
|
- **Latency Metrics**: Monitor response times
|
|
- **Quality Metrics**: Track response quality
|
|
- **Cost Analysis**: Analyze cost per request
|
|
|
|
## See Also
|
|
|
|
<CardGroup cols={2}>
|
|
<Card title="Services" icon="server" href="/runtime/services">
|
|
Build services that provide models
|
|
</Card>
|
|
|
|
<Card title="Messaging" icon="message" href="/runtime/events">
|
|
Stream model responses in real-time
|
|
</Card>
|
|
|
|
<Card title="Sessions API" icon="comments" href="/cli/overview">
|
|
Use models in conversations
|
|
</Card>
|
|
|
|
<Card title="Providers" icon="database" href="/runtime/providers">
|
|
Supply context to models
|
|
</Card>
|
|
</CardGroup>
|