agentic-qe 2.4.0 → 2.5.1
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/.claude/agents/qe-a11y-ally.md +855 -0
- package/.claude/agents/qx-partner.md +120 -4
- package/.claude/skills/testability-scoring/SKILL.md +107 -6
- package/CHANGELOG.md +135 -0
- package/README.md +7 -6
- package/dist/agents/AccessibilityAllyAgent.d.ts +168 -0
- package/dist/agents/AccessibilityAllyAgent.d.ts.map +1 -0
- package/dist/agents/AccessibilityAllyAgent.js +462 -0
- package/dist/agents/AccessibilityAllyAgent.js.map +1 -0
- package/dist/agents/SONAIntegration.d.ts +109 -0
- package/dist/agents/SONAIntegration.d.ts.map +1 -0
- package/dist/agents/SONAIntegration.js +167 -0
- package/dist/agents/SONAIntegration.js.map +1 -0
- package/dist/agents/index.d.ts +3 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +93 -2
- package/dist/agents/index.js.map +1 -1
- package/dist/cli/init/agents.js +1 -1
- package/dist/cli/init/claude-config.js +2 -2
- package/dist/cli/init/database-init.js +1 -1
- package/dist/core/cache/BinaryCacheImpl.d.ts +161 -0
- package/dist/core/cache/BinaryCacheImpl.d.ts.map +1 -0
- package/dist/core/cache/BinaryCacheImpl.js +685 -0
- package/dist/core/cache/BinaryCacheImpl.js.map +1 -0
- package/dist/core/cache/BinaryMetadataCache.d.ts +244 -0
- package/dist/core/cache/BinaryMetadataCache.d.ts.map +1 -1
- package/dist/core/cache/BinaryMetadataCache.js +63 -1
- package/dist/core/cache/BinaryMetadataCache.js.map +1 -1
- package/dist/core/cache/index.d.ts +1 -0
- package/dist/core/cache/index.d.ts.map +1 -1
- package/dist/core/cache/index.js +10 -1
- package/dist/core/cache/index.js.map +1 -1
- package/dist/core/memory/AgentDBService.d.ts +30 -4
- package/dist/core/memory/AgentDBService.d.ts.map +1 -1
- package/dist/core/memory/AgentDBService.js +122 -12
- package/dist/core/memory/AgentDBService.js.map +1 -1
- package/dist/core/memory/CachedHNSWVectorMemory.d.ts +153 -0
- package/dist/core/memory/CachedHNSWVectorMemory.d.ts.map +1 -0
- package/dist/core/memory/CachedHNSWVectorMemory.js +329 -0
- package/dist/core/memory/CachedHNSWVectorMemory.js.map +1 -0
- package/dist/core/memory/HNSWVectorMemory.js +1 -1
- package/dist/core/memory/RuVectorPatternStore.d.ts.map +1 -1
- package/dist/core/memory/RuVectorPatternStore.js +8 -2
- package/dist/core/memory/RuVectorPatternStore.js.map +1 -1
- package/dist/core/memory/UnifiedMemoryCoordinator.d.ts +50 -0
- package/dist/core/memory/UnifiedMemoryCoordinator.d.ts.map +1 -1
- package/dist/core/memory/UnifiedMemoryCoordinator.js +206 -0
- package/dist/core/memory/UnifiedMemoryCoordinator.js.map +1 -1
- package/dist/core/memory/index.d.ts +2 -0
- package/dist/core/memory/index.d.ts.map +1 -1
- package/dist/core/memory/index.js +8 -1
- package/dist/core/memory/index.js.map +1 -1
- package/dist/core/optimization/RecursiveOptimizer.d.ts +233 -0
- package/dist/core/optimization/RecursiveOptimizer.d.ts.map +1 -0
- package/dist/core/optimization/RecursiveOptimizer.js +509 -0
- package/dist/core/optimization/RecursiveOptimizer.js.map +1 -0
- package/dist/core/strategies/SONALearningStrategy.d.ts +115 -0
- package/dist/core/strategies/SONALearningStrategy.d.ts.map +1 -0
- package/dist/core/strategies/SONALearningStrategy.js +656 -0
- package/dist/core/strategies/SONALearningStrategy.js.map +1 -0
- package/dist/core/strategies/TRMLearningStrategy.d.ts +162 -0
- package/dist/core/strategies/TRMLearningStrategy.d.ts.map +1 -0
- package/dist/core/strategies/TRMLearningStrategy.js +670 -0
- package/dist/core/strategies/TRMLearningStrategy.js.map +1 -0
- package/dist/core/strategies/index.d.ts +10 -1
- package/dist/core/strategies/index.d.ts.map +1 -1
- package/dist/core/strategies/index.js +4 -1
- package/dist/core/strategies/index.js.map +1 -1
- package/dist/learning/SONAFeedbackLoop.d.ts +168 -0
- package/dist/learning/SONAFeedbackLoop.d.ts.map +1 -0
- package/dist/learning/SONAFeedbackLoop.js +344 -0
- package/dist/learning/SONAFeedbackLoop.js.map +1 -0
- package/dist/learning/baselines/BaselineCollector.d.ts +1 -1
- package/dist/learning/baselines/BaselineCollector.js +1 -1
- package/dist/learning/baselines/StandardTaskSuite.d.ts +1 -1
- package/dist/learning/baselines/StandardTaskSuite.js +1 -1
- package/dist/learning/index.d.ts +2 -0
- package/dist/learning/index.d.ts.map +1 -1
- package/dist/learning/index.js +6 -1
- package/dist/learning/index.js.map +1 -1
- package/dist/mcp/server-instructions.d.ts +1 -1
- package/dist/mcp/server-instructions.js +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +23 -16
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/services/AgentRegistry.d.ts.map +1 -1
- package/dist/mcp/services/AgentRegistry.js +6 -1
- package/dist/mcp/services/AgentRegistry.js.map +1 -1
- package/dist/mcp/tools/qe/accessibility/accname-computation.d.ts +114 -0
- package/dist/mcp/tools/qe/accessibility/accname-computation.d.ts.map +1 -0
- package/dist/mcp/tools/qe/accessibility/accname-computation.js +566 -0
- package/dist/mcp/tools/qe/accessibility/accname-computation.js.map +1 -0
- package/dist/mcp/tools/qe/accessibility/apg-patterns.d.ts +103 -0
- package/dist/mcp/tools/qe/accessibility/apg-patterns.d.ts.map +1 -0
- package/dist/mcp/tools/qe/accessibility/apg-patterns.js +1028 -0
- package/dist/mcp/tools/qe/accessibility/apg-patterns.js.map +1 -0
- package/dist/mcp/tools/qe/accessibility/en-301-549-mapping.d.ts +48 -0
- package/dist/mcp/tools/qe/accessibility/en-301-549-mapping.d.ts.map +1 -0
- package/dist/mcp/tools/qe/accessibility/en-301-549-mapping.js +565 -0
- package/dist/mcp/tools/qe/accessibility/en-301-549-mapping.js.map +1 -0
- package/dist/mcp/tools/qe/accessibility/eu-accessibility-act.d.ts +117 -0
- package/dist/mcp/tools/qe/accessibility/eu-accessibility-act.d.ts.map +1 -0
- package/dist/mcp/tools/qe/accessibility/eu-accessibility-act.js +571 -0
- package/dist/mcp/tools/qe/accessibility/eu-accessibility-act.js.map +1 -0
- package/dist/mcp/tools/qe/accessibility/html-report-generator.d.ts +23 -0
- package/dist/mcp/tools/qe/accessibility/html-report-generator.d.ts.map +1 -0
- package/dist/mcp/tools/qe/accessibility/html-report-generator.js +1152 -0
- package/dist/mcp/tools/qe/accessibility/html-report-generator.js.map +1 -0
- package/dist/mcp/tools/qe/accessibility/index.d.ts +22 -0
- package/dist/mcp/tools/qe/accessibility/index.d.ts.map +1 -0
- package/dist/mcp/tools/qe/accessibility/index.js +38 -0
- package/dist/mcp/tools/qe/accessibility/index.js.map +1 -0
- package/dist/mcp/tools/qe/accessibility/markdown-report-generator.d.ts +18 -0
- package/dist/mcp/tools/qe/accessibility/markdown-report-generator.d.ts.map +1 -0
- package/dist/mcp/tools/qe/accessibility/markdown-report-generator.js +549 -0
- package/dist/mcp/tools/qe/accessibility/markdown-report-generator.js.map +1 -0
- package/dist/mcp/tools/qe/accessibility/remediation-code-generator.d.ts +139 -0
- package/dist/mcp/tools/qe/accessibility/remediation-code-generator.d.ts.map +1 -0
- package/dist/mcp/tools/qe/accessibility/remediation-code-generator.js +1300 -0
- package/dist/mcp/tools/qe/accessibility/remediation-code-generator.js.map +1 -0
- package/dist/mcp/tools/qe/accessibility/scan-comprehensive.d.ts +138 -0
- package/dist/mcp/tools/qe/accessibility/scan-comprehensive.d.ts.map +1 -0
- package/dist/mcp/tools/qe/accessibility/scan-comprehensive.js +1298 -0
- package/dist/mcp/tools/qe/accessibility/scan-comprehensive.js.map +1 -0
- package/dist/mcp/tools/qe/accessibility/video-vision-analyzer.d.ts +50 -0
- package/dist/mcp/tools/qe/accessibility/video-vision-analyzer.d.ts.map +1 -0
- package/dist/mcp/tools/qe/accessibility/video-vision-analyzer.js +469 -0
- package/dist/mcp/tools/qe/accessibility/video-vision-analyzer.js.map +1 -0
- package/dist/mcp/tools/qe/accessibility/webvtt-generator.d.ts +193 -0
- package/dist/mcp/tools/qe/accessibility/webvtt-generator.d.ts.map +1 -0
- package/dist/mcp/tools/qe/accessibility/webvtt-generator.js +511 -0
- package/dist/mcp/tools/qe/accessibility/webvtt-generator.js.map +1 -0
- package/dist/mcp/tools.d.ts +1 -0
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +61 -0
- package/dist/mcp/tools.js.map +1 -1
- package/dist/providers/HybridRouter.d.ts +34 -3
- package/dist/providers/HybridRouter.d.ts.map +1 -1
- package/dist/providers/HybridRouter.js +69 -4
- package/dist/providers/HybridRouter.js.map +1 -1
- package/dist/providers/LLMProviderFactory.d.ts +68 -1
- package/dist/providers/LLMProviderFactory.d.ts.map +1 -1
- package/dist/providers/LLMProviderFactory.js +173 -6
- package/dist/providers/LLMProviderFactory.js.map +1 -1
- package/dist/providers/OpenRouterProvider.d.ts +150 -0
- package/dist/providers/OpenRouterProvider.d.ts.map +1 -0
- package/dist/providers/OpenRouterProvider.js +545 -0
- package/dist/providers/OpenRouterProvider.js.map +1 -0
- package/dist/providers/RuvllmProvider.d.ts +130 -16
- package/dist/providers/RuvllmProvider.d.ts.map +1 -1
- package/dist/providers/RuvllmProvider.js +399 -83
- package/dist/providers/RuvllmProvider.js.map +1 -1
- package/dist/providers/index.d.ts +33 -4
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +72 -21
- package/dist/providers/index.js.map +1 -1
- package/dist/telemetry/instrumentation/agent.d.ts +1 -1
- package/dist/telemetry/instrumentation/agent.js +1 -1
- package/dist/telemetry/instrumentation/index.d.ts +1 -1
- package/dist/telemetry/instrumentation/index.js +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/ruvllm.d.ts +97 -0
- package/dist/types/ruvllm.d.ts.map +1 -0
- package/dist/types/ruvllm.js +46 -0
- package/dist/types/ruvllm.js.map +1 -0
- package/dist/utils/ruvllm-loader.d.ts +94 -0
- package/dist/utils/ruvllm-loader.d.ts.map +1 -0
- package/dist/utils/ruvllm-loader.js +87 -0
- package/dist/utils/ruvllm-loader.js.map +1 -0
- package/docs/reference/agents.md +36 -1
- package/package.json +5 -2
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenRouterProvider - OpenRouter API Implementation
|
|
3
|
+
*
|
|
4
|
+
* Provides LLM capabilities through OpenRouter's unified API with support for:
|
|
5
|
+
* - 300+ models from multiple providers (OpenAI, Anthropic, Google, etc.)
|
|
6
|
+
* - Model hot-swapping at runtime
|
|
7
|
+
* - Auto-routing to cheapest capable model
|
|
8
|
+
* - Streaming responses
|
|
9
|
+
* - Cost tracking
|
|
10
|
+
*
|
|
11
|
+
* @module providers/OpenRouterProvider
|
|
12
|
+
* @version 1.0.0
|
|
13
|
+
*/
|
|
14
|
+
import { ILLMProvider, LLMProviderConfig, LLMCompletionOptions, LLMCompletionResponse, LLMStreamEvent, LLMEmbeddingOptions, LLMEmbeddingResponse, LLMTokenCountOptions, LLMHealthStatus, LLMProviderMetadata } from './ILLMProvider';
|
|
15
|
+
/**
|
|
16
|
+
* OpenRouter model information
|
|
17
|
+
*/
|
|
18
|
+
export interface OpenRouterModel {
|
|
19
|
+
/** Model ID (e.g., 'anthropic/claude-3.5-sonnet') */
|
|
20
|
+
id: string;
|
|
21
|
+
/** Display name */
|
|
22
|
+
name: string;
|
|
23
|
+
/** Provider (e.g., 'Anthropic', 'OpenAI') */
|
|
24
|
+
provider?: string;
|
|
25
|
+
/** Context length */
|
|
26
|
+
contextLength: number;
|
|
27
|
+
/** Pricing per million tokens */
|
|
28
|
+
pricing: {
|
|
29
|
+
prompt: number;
|
|
30
|
+
completion: number;
|
|
31
|
+
};
|
|
32
|
+
/** Supported modalities */
|
|
33
|
+
modalities?: string[];
|
|
34
|
+
/** Whether model supports tool use */
|
|
35
|
+
supportsToolUse?: boolean;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* OpenRouter-specific configuration
|
|
39
|
+
*/
|
|
40
|
+
export interface OpenRouterConfig extends LLMProviderConfig {
|
|
41
|
+
/** OpenRouter API key (uses OPENROUTER_API_KEY env var if not provided) */
|
|
42
|
+
apiKey?: string;
|
|
43
|
+
/** Default model to use (default: auto-selects cheapest capable) */
|
|
44
|
+
defaultModel?: string;
|
|
45
|
+
/** Your site URL for OpenRouter rankings */
|
|
46
|
+
siteUrl?: string;
|
|
47
|
+
/** Your site name */
|
|
48
|
+
siteName?: string;
|
|
49
|
+
/** Fallback models if primary unavailable */
|
|
50
|
+
fallbackModels?: string[];
|
|
51
|
+
/** Enable model discovery (fetch available models on init) */
|
|
52
|
+
enableModelDiscovery?: boolean;
|
|
53
|
+
/** Base URL for API (default: https://openrouter.ai/api/v1) */
|
|
54
|
+
baseUrl?: string;
|
|
55
|
+
/** Enable auto-routing to cheapest model */
|
|
56
|
+
enableAutoRoute?: boolean;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* OpenRouterProvider - OpenRouter API implementation of ILLMProvider
|
|
60
|
+
*
|
|
61
|
+
* Enables access to 300+ models through OpenRouter's unified API
|
|
62
|
+
* with support for runtime model switching and cost optimization.
|
|
63
|
+
*/
|
|
64
|
+
export declare class OpenRouterProvider implements ILLMProvider {
|
|
65
|
+
private readonly logger;
|
|
66
|
+
private config;
|
|
67
|
+
private isInitialized;
|
|
68
|
+
private totalCost;
|
|
69
|
+
private requestCount;
|
|
70
|
+
private apiKey?;
|
|
71
|
+
private currentModel;
|
|
72
|
+
private availableModels;
|
|
73
|
+
constructor(config?: OpenRouterConfig);
|
|
74
|
+
/**
|
|
75
|
+
* Initialize the OpenRouter provider
|
|
76
|
+
*/
|
|
77
|
+
initialize(): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Discover available models from OpenRouter API
|
|
80
|
+
*/
|
|
81
|
+
private discoverModels;
|
|
82
|
+
/**
|
|
83
|
+
* Complete a prompt
|
|
84
|
+
*/
|
|
85
|
+
complete(options: LLMCompletionOptions): Promise<LLMCompletionResponse>;
|
|
86
|
+
/**
|
|
87
|
+
* Stream a completion
|
|
88
|
+
*/
|
|
89
|
+
streamComplete(options: LLMCompletionOptions): AsyncIterableIterator<LLMStreamEvent>;
|
|
90
|
+
/**
|
|
91
|
+
* Generate embeddings
|
|
92
|
+
* Note: OpenRouter embedding support varies by model
|
|
93
|
+
*/
|
|
94
|
+
embed(options: LLMEmbeddingOptions): Promise<LLMEmbeddingResponse>;
|
|
95
|
+
/**
|
|
96
|
+
* Count tokens (approximation using character count)
|
|
97
|
+
*/
|
|
98
|
+
countTokens(options: LLMTokenCountOptions): Promise<number>;
|
|
99
|
+
/**
|
|
100
|
+
* Health check
|
|
101
|
+
*/
|
|
102
|
+
healthCheck(): Promise<LLMHealthStatus>;
|
|
103
|
+
/**
|
|
104
|
+
* Get provider metadata
|
|
105
|
+
*/
|
|
106
|
+
getMetadata(): LLMProviderMetadata;
|
|
107
|
+
/**
|
|
108
|
+
* Shutdown the provider
|
|
109
|
+
*/
|
|
110
|
+
shutdown(): Promise<void>;
|
|
111
|
+
/**
|
|
112
|
+
* Track cost for a request
|
|
113
|
+
*/
|
|
114
|
+
trackCost(usage: LLMCompletionResponse['usage']): number;
|
|
115
|
+
/**
|
|
116
|
+
* Hot-swap to a different model at runtime
|
|
117
|
+
*/
|
|
118
|
+
setModel(model: string): Promise<void>;
|
|
119
|
+
/**
|
|
120
|
+
* Get current model
|
|
121
|
+
*/
|
|
122
|
+
getCurrentModel(): string;
|
|
123
|
+
/**
|
|
124
|
+
* Get available models
|
|
125
|
+
*/
|
|
126
|
+
getAvailableModels(): Promise<OpenRouterModel[]>;
|
|
127
|
+
/**
|
|
128
|
+
* Get model info
|
|
129
|
+
*/
|
|
130
|
+
getModelInfo(model: string): OpenRouterModel | null;
|
|
131
|
+
/**
|
|
132
|
+
* Get total cost tracked
|
|
133
|
+
*/
|
|
134
|
+
getTotalCost(): number;
|
|
135
|
+
/**
|
|
136
|
+
* Get request count
|
|
137
|
+
*/
|
|
138
|
+
getRequestCount(): number;
|
|
139
|
+
private ensureInitialized;
|
|
140
|
+
private buildMessages;
|
|
141
|
+
private getHeaders;
|
|
142
|
+
private makeRequest;
|
|
143
|
+
private mapStopReason;
|
|
144
|
+
private handleError;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Create an OpenRouter provider with configuration
|
|
148
|
+
*/
|
|
149
|
+
export declare function createOpenRouterProvider(config?: OpenRouterConfig): OpenRouterProvider;
|
|
150
|
+
//# sourceMappingURL=OpenRouterProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpenRouterProvider.d.ts","sourceRoot":"","sources":["../../src/providers/OpenRouterProvider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OpenRouterProvider - OpenRouter API Implementation
|
|
4
|
+
*
|
|
5
|
+
* Provides LLM capabilities through OpenRouter's unified API with support for:
|
|
6
|
+
* - 300+ models from multiple providers (OpenAI, Anthropic, Google, etc.)
|
|
7
|
+
* - Model hot-swapping at runtime
|
|
8
|
+
* - Auto-routing to cheapest capable model
|
|
9
|
+
* - Streaming responses
|
|
10
|
+
* - Cost tracking
|
|
11
|
+
*
|
|
12
|
+
* @module providers/OpenRouterProvider
|
|
13
|
+
* @version 1.0.0
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.OpenRouterProvider = void 0;
|
|
17
|
+
exports.createOpenRouterProvider = createOpenRouterProvider;
|
|
18
|
+
const ILLMProvider_1 = require("./ILLMProvider");
|
|
19
|
+
const Logger_1 = require("../utils/Logger");
|
|
20
|
+
/**
|
|
21
|
+
* Default OpenRouter pricing (per million tokens)
|
|
22
|
+
* These are fallback values; actual prices come from the API
|
|
23
|
+
*/
|
|
24
|
+
const DEFAULT_PRICING = {
|
|
25
|
+
'anthropic/claude-3.5-sonnet': { input: 3.0, output: 15.0 },
|
|
26
|
+
'anthropic/claude-3-haiku': { input: 0.25, output: 1.25 },
|
|
27
|
+
'openai/gpt-4o': { input: 2.5, output: 10.0 },
|
|
28
|
+
'openai/gpt-4o-mini': { input: 0.15, output: 0.6 },
|
|
29
|
+
'google/gemini-pro-1.5': { input: 1.25, output: 5.0 },
|
|
30
|
+
'meta-llama/llama-3.1-70b-instruct': { input: 0.52, output: 0.75 },
|
|
31
|
+
'auto': { input: 0.5, output: 2.0 }, // Average estimate for auto-routing
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* OpenRouterProvider - OpenRouter API implementation of ILLMProvider
|
|
35
|
+
*
|
|
36
|
+
* Enables access to 300+ models through OpenRouter's unified API
|
|
37
|
+
* with support for runtime model switching and cost optimization.
|
|
38
|
+
*/
|
|
39
|
+
class OpenRouterProvider {
|
|
40
|
+
constructor(config = {}) {
|
|
41
|
+
this.availableModels = new Map();
|
|
42
|
+
this.logger = Logger_1.Logger.getInstance();
|
|
43
|
+
this.config = {
|
|
44
|
+
name: config.name || 'openrouter',
|
|
45
|
+
debug: config.debug ?? false,
|
|
46
|
+
timeout: config.timeout ?? 60000,
|
|
47
|
+
maxRetries: config.maxRetries ?? 3,
|
|
48
|
+
defaultModel: config.defaultModel || 'auto',
|
|
49
|
+
siteUrl: config.siteUrl || process.env.OPENROUTER_SITE_URL,
|
|
50
|
+
siteName: config.siteName || process.env.OPENROUTER_SITE_NAME || 'Agentic-QE-Fleet',
|
|
51
|
+
fallbackModels: config.fallbackModels || [
|
|
52
|
+
'anthropic/claude-3.5-sonnet',
|
|
53
|
+
'openai/gpt-4o-mini',
|
|
54
|
+
'meta-llama/llama-3.1-70b-instruct'
|
|
55
|
+
],
|
|
56
|
+
enableModelDiscovery: config.enableModelDiscovery ?? true,
|
|
57
|
+
baseUrl: config.baseUrl || 'https://openrouter.ai/api/v1',
|
|
58
|
+
enableAutoRoute: config.enableAutoRoute ?? true,
|
|
59
|
+
apiKey: config.apiKey,
|
|
60
|
+
};
|
|
61
|
+
this.currentModel = this.config.defaultModel;
|
|
62
|
+
this.isInitialized = false;
|
|
63
|
+
this.totalCost = 0;
|
|
64
|
+
this.requestCount = 0;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Initialize the OpenRouter provider
|
|
68
|
+
*/
|
|
69
|
+
async initialize() {
|
|
70
|
+
if (this.isInitialized) {
|
|
71
|
+
this.logger.warn('OpenRouterProvider already initialized');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
this.apiKey = this.config.apiKey || process.env.OPENROUTER_API_KEY;
|
|
75
|
+
if (!this.apiKey) {
|
|
76
|
+
throw new ILLMProvider_1.LLMProviderError('OpenRouter API key not provided. Set OPENROUTER_API_KEY environment variable.', 'openrouter', 'AUTH_ERROR', false);
|
|
77
|
+
}
|
|
78
|
+
// Optionally discover available models
|
|
79
|
+
if (this.config.enableModelDiscovery) {
|
|
80
|
+
try {
|
|
81
|
+
await this.discoverModels();
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
this.logger.warn('Failed to discover models, using defaults:', error);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
this.isInitialized = true;
|
|
88
|
+
this.logger.info('OpenRouterProvider initialized', {
|
|
89
|
+
model: this.currentModel,
|
|
90
|
+
autoRoute: this.config.enableAutoRoute,
|
|
91
|
+
modelsDiscovered: this.availableModels.size
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Discover available models from OpenRouter API
|
|
96
|
+
*/
|
|
97
|
+
async discoverModels() {
|
|
98
|
+
try {
|
|
99
|
+
const response = await fetch(`${this.config.baseUrl}/models`, {
|
|
100
|
+
method: 'GET',
|
|
101
|
+
headers: {
|
|
102
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
if (!response.ok) {
|
|
106
|
+
throw new Error(`Failed to fetch models: ${response.status}`);
|
|
107
|
+
}
|
|
108
|
+
const data = await response.json();
|
|
109
|
+
for (const model of data.data || []) {
|
|
110
|
+
this.availableModels.set(model.id, {
|
|
111
|
+
id: model.id,
|
|
112
|
+
name: model.name || model.id,
|
|
113
|
+
contextLength: model.context_length || 4096,
|
|
114
|
+
pricing: {
|
|
115
|
+
prompt: (model.pricing?.prompt || 0) * 1000000,
|
|
116
|
+
completion: (model.pricing?.completion || 0) * 1000000,
|
|
117
|
+
},
|
|
118
|
+
modalities: model.modalities,
|
|
119
|
+
supportsToolUse: model.supports_tool_use,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
this.logger.debug(`Discovered ${this.availableModels.size} models from OpenRouter`);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
this.logger.warn('Model discovery failed:', error);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Complete a prompt
|
|
130
|
+
*/
|
|
131
|
+
async complete(options) {
|
|
132
|
+
this.ensureInitialized();
|
|
133
|
+
const model = options.model || this.currentModel;
|
|
134
|
+
const startTime = Date.now();
|
|
135
|
+
try {
|
|
136
|
+
// Build request body (OpenAI-compatible format)
|
|
137
|
+
const messages = this.buildMessages(options);
|
|
138
|
+
const body = {
|
|
139
|
+
model,
|
|
140
|
+
messages,
|
|
141
|
+
max_tokens: options.maxTokens || 4096,
|
|
142
|
+
temperature: options.temperature ?? 0.7,
|
|
143
|
+
};
|
|
144
|
+
// Add system prompt if provided
|
|
145
|
+
if (options.system && options.system.length > 0) {
|
|
146
|
+
const systemText = options.system.map(s => s.text).join('\n');
|
|
147
|
+
messages.unshift({ role: 'system', content: systemText });
|
|
148
|
+
}
|
|
149
|
+
const response = await this.makeRequest('/chat/completions', body);
|
|
150
|
+
// Convert OpenRouter response to our format
|
|
151
|
+
const choice = response.choices?.[0];
|
|
152
|
+
const content = choice?.message?.content || '';
|
|
153
|
+
const result = {
|
|
154
|
+
content: [{ type: 'text', text: content }],
|
|
155
|
+
usage: {
|
|
156
|
+
input_tokens: response.usage?.prompt_tokens || 0,
|
|
157
|
+
output_tokens: response.usage?.completion_tokens || 0,
|
|
158
|
+
},
|
|
159
|
+
model: response.model || model,
|
|
160
|
+
stop_reason: this.mapStopReason(choice?.finish_reason),
|
|
161
|
+
id: response.id || `or-${Date.now()}`,
|
|
162
|
+
metadata: {
|
|
163
|
+
latency: Date.now() - startTime,
|
|
164
|
+
provider: 'openrouter',
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
// Track cost
|
|
168
|
+
this.trackCost(result.usage);
|
|
169
|
+
this.requestCount++;
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
this.handleError(error, 'complete');
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Stream a completion
|
|
179
|
+
*/
|
|
180
|
+
async *streamComplete(options) {
|
|
181
|
+
this.ensureInitialized();
|
|
182
|
+
const model = options.model || this.currentModel;
|
|
183
|
+
try {
|
|
184
|
+
const messages = this.buildMessages(options);
|
|
185
|
+
const body = {
|
|
186
|
+
model,
|
|
187
|
+
messages,
|
|
188
|
+
max_tokens: options.maxTokens || 4096,
|
|
189
|
+
temperature: options.temperature ?? 0.7,
|
|
190
|
+
stream: true,
|
|
191
|
+
};
|
|
192
|
+
// Add system prompt if provided
|
|
193
|
+
if (options.system && options.system.length > 0) {
|
|
194
|
+
const systemText = options.system.map(s => s.text).join('\n');
|
|
195
|
+
messages.unshift({ role: 'system', content: systemText });
|
|
196
|
+
}
|
|
197
|
+
const response = await fetch(`${this.config.baseUrl}/chat/completions`, {
|
|
198
|
+
method: 'POST',
|
|
199
|
+
headers: this.getHeaders(),
|
|
200
|
+
body: JSON.stringify(body),
|
|
201
|
+
});
|
|
202
|
+
if (!response.ok) {
|
|
203
|
+
throw new ILLMProvider_1.LLMProviderError(`OpenRouter request failed: ${response.status}`, 'openrouter', 'REQUEST_ERROR', response.status >= 500);
|
|
204
|
+
}
|
|
205
|
+
const reader = response.body?.getReader();
|
|
206
|
+
if (!reader) {
|
|
207
|
+
throw new ILLMProvider_1.LLMProviderError('No response body for streaming', 'openrouter', 'STREAM_ERROR', false);
|
|
208
|
+
}
|
|
209
|
+
const decoder = new TextDecoder();
|
|
210
|
+
let buffer = '';
|
|
211
|
+
// Emit message start
|
|
212
|
+
yield {
|
|
213
|
+
type: 'message_start',
|
|
214
|
+
message: {
|
|
215
|
+
id: `or-stream-${Date.now()}`,
|
|
216
|
+
model,
|
|
217
|
+
content: [],
|
|
218
|
+
usage: { input_tokens: 0, output_tokens: 0 },
|
|
219
|
+
stop_reason: 'end_turn',
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
// Emit content block start
|
|
223
|
+
yield {
|
|
224
|
+
type: 'content_block_start',
|
|
225
|
+
content_block: { type: 'text', text: '' },
|
|
226
|
+
};
|
|
227
|
+
while (true) {
|
|
228
|
+
const { done, value } = await reader.read();
|
|
229
|
+
if (done)
|
|
230
|
+
break;
|
|
231
|
+
buffer += decoder.decode(value, { stream: true });
|
|
232
|
+
const lines = buffer.split('\n');
|
|
233
|
+
buffer = lines.pop() || '';
|
|
234
|
+
for (const line of lines) {
|
|
235
|
+
if (line.startsWith('data: ')) {
|
|
236
|
+
const data = line.slice(6).trim();
|
|
237
|
+
if (data === '[DONE]') {
|
|
238
|
+
yield { type: 'content_block_stop' };
|
|
239
|
+
yield { type: 'message_stop' };
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
try {
|
|
243
|
+
const parsed = JSON.parse(data);
|
|
244
|
+
const delta = parsed.choices?.[0]?.delta?.content;
|
|
245
|
+
if (delta) {
|
|
246
|
+
yield {
|
|
247
|
+
type: 'content_block_delta',
|
|
248
|
+
delta: { type: 'text_delta', text: delta },
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
// Skip malformed JSON
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
yield { type: 'content_block_stop' };
|
|
259
|
+
yield { type: 'message_stop' };
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
this.handleError(error, 'streamComplete');
|
|
263
|
+
throw error;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Generate embeddings
|
|
268
|
+
* Note: OpenRouter embedding support varies by model
|
|
269
|
+
*/
|
|
270
|
+
async embed(options) {
|
|
271
|
+
this.ensureInitialized();
|
|
272
|
+
const model = options.model || 'openai/text-embedding-3-small';
|
|
273
|
+
try {
|
|
274
|
+
const response = await this.makeRequest('/embeddings', {
|
|
275
|
+
model,
|
|
276
|
+
input: options.text,
|
|
277
|
+
dimensions: options.dimensions,
|
|
278
|
+
});
|
|
279
|
+
const embedding = response.data?.[0]?.embedding || [];
|
|
280
|
+
return {
|
|
281
|
+
embedding,
|
|
282
|
+
model: response.model || model,
|
|
283
|
+
tokens: response.usage?.prompt_tokens || 0,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
// Embeddings may not be available for all models
|
|
288
|
+
throw new ILLMProvider_1.LLMProviderError(`Embeddings not available: ${error.message}`, 'openrouter', 'EMBEDDINGS_UNAVAILABLE', false);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Count tokens (approximation using character count)
|
|
293
|
+
*/
|
|
294
|
+
async countTokens(options) {
|
|
295
|
+
// OpenRouter doesn't have a token counting endpoint
|
|
296
|
+
// Use approximation: ~4 characters per token for English
|
|
297
|
+
return Math.ceil(options.text.length / 4);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Health check
|
|
301
|
+
*/
|
|
302
|
+
async healthCheck() {
|
|
303
|
+
const startTime = Date.now();
|
|
304
|
+
if (!this.isInitialized) {
|
|
305
|
+
return {
|
|
306
|
+
healthy: false,
|
|
307
|
+
error: 'Provider not initialized',
|
|
308
|
+
timestamp: new Date(),
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
try {
|
|
312
|
+
// Check API health by fetching models endpoint
|
|
313
|
+
const response = await fetch(`${this.config.baseUrl}/models`, {
|
|
314
|
+
method: 'GET',
|
|
315
|
+
headers: {
|
|
316
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
317
|
+
},
|
|
318
|
+
signal: AbortSignal.timeout(5000),
|
|
319
|
+
});
|
|
320
|
+
const healthy = response.ok;
|
|
321
|
+
return {
|
|
322
|
+
healthy,
|
|
323
|
+
latency: Date.now() - startTime,
|
|
324
|
+
timestamp: new Date(),
|
|
325
|
+
metadata: {
|
|
326
|
+
statusCode: response.status,
|
|
327
|
+
requestCount: this.requestCount,
|
|
328
|
+
totalCost: this.totalCost,
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
return {
|
|
334
|
+
healthy: false,
|
|
335
|
+
error: error.message,
|
|
336
|
+
timestamp: new Date(),
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Get provider metadata
|
|
342
|
+
*/
|
|
343
|
+
getMetadata() {
|
|
344
|
+
const models = this.availableModels.size > 0
|
|
345
|
+
? Array.from(this.availableModels.keys())
|
|
346
|
+
: Object.keys(DEFAULT_PRICING);
|
|
347
|
+
// Get pricing for current model
|
|
348
|
+
const modelInfo = this.availableModels.get(this.currentModel);
|
|
349
|
+
const defaultPricing = DEFAULT_PRICING[this.currentModel] || DEFAULT_PRICING['auto'];
|
|
350
|
+
return {
|
|
351
|
+
name: 'openrouter',
|
|
352
|
+
version: '1.0.0',
|
|
353
|
+
models,
|
|
354
|
+
capabilities: {
|
|
355
|
+
streaming: true,
|
|
356
|
+
caching: false, // OpenRouter doesn't support prompt caching
|
|
357
|
+
embeddings: true,
|
|
358
|
+
vision: true, // Many models support vision
|
|
359
|
+
},
|
|
360
|
+
costs: {
|
|
361
|
+
inputPerMillion: modelInfo?.pricing.prompt || defaultPricing.input,
|
|
362
|
+
outputPerMillion: modelInfo?.pricing.completion || defaultPricing.output,
|
|
363
|
+
},
|
|
364
|
+
location: 'cloud',
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Shutdown the provider
|
|
369
|
+
*/
|
|
370
|
+
async shutdown() {
|
|
371
|
+
this.logger.info('OpenRouterProvider shutting down', {
|
|
372
|
+
totalRequests: this.requestCount,
|
|
373
|
+
totalCost: this.totalCost,
|
|
374
|
+
});
|
|
375
|
+
this.isInitialized = false;
|
|
376
|
+
this.availableModels.clear();
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Track cost for a request
|
|
380
|
+
*/
|
|
381
|
+
trackCost(usage) {
|
|
382
|
+
const modelInfo = this.availableModels.get(this.currentModel);
|
|
383
|
+
const defaultPricing = DEFAULT_PRICING[this.currentModel] || DEFAULT_PRICING['auto'];
|
|
384
|
+
const inputCost = (usage.input_tokens / 1000000) *
|
|
385
|
+
(modelInfo?.pricing.prompt || defaultPricing.input);
|
|
386
|
+
const outputCost = (usage.output_tokens / 1000000) *
|
|
387
|
+
(modelInfo?.pricing.completion || defaultPricing.output);
|
|
388
|
+
const cost = inputCost + outputCost;
|
|
389
|
+
this.totalCost += cost;
|
|
390
|
+
return cost;
|
|
391
|
+
}
|
|
392
|
+
// ============================================
|
|
393
|
+
// Hot-Swap Methods (G6)
|
|
394
|
+
// ============================================
|
|
395
|
+
/**
|
|
396
|
+
* Hot-swap to a different model at runtime
|
|
397
|
+
*/
|
|
398
|
+
async setModel(model) {
|
|
399
|
+
if (!this.isInitialized) {
|
|
400
|
+
throw new ILLMProvider_1.LLMProviderError('Provider not initialized', 'openrouter', 'NOT_INITIALIZED', false);
|
|
401
|
+
}
|
|
402
|
+
// Validate model exists (if we have model info)
|
|
403
|
+
if (this.availableModels.size > 0 && !this.availableModels.has(model) && model !== 'auto') {
|
|
404
|
+
this.logger.warn(`Model ${model} not found in available models, proceeding anyway`);
|
|
405
|
+
}
|
|
406
|
+
const previousModel = this.currentModel;
|
|
407
|
+
this.currentModel = model;
|
|
408
|
+
this.logger.info('Model hot-swapped', {
|
|
409
|
+
from: previousModel,
|
|
410
|
+
to: model,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Get current model
|
|
415
|
+
*/
|
|
416
|
+
getCurrentModel() {
|
|
417
|
+
return this.currentModel;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Get available models
|
|
421
|
+
*/
|
|
422
|
+
async getAvailableModels() {
|
|
423
|
+
if (this.availableModels.size === 0) {
|
|
424
|
+
await this.discoverModels();
|
|
425
|
+
}
|
|
426
|
+
return Array.from(this.availableModels.values());
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Get model info
|
|
430
|
+
*/
|
|
431
|
+
getModelInfo(model) {
|
|
432
|
+
return this.availableModels.get(model) || null;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Get total cost tracked
|
|
436
|
+
*/
|
|
437
|
+
getTotalCost() {
|
|
438
|
+
return this.totalCost;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Get request count
|
|
442
|
+
*/
|
|
443
|
+
getRequestCount() {
|
|
444
|
+
return this.requestCount;
|
|
445
|
+
}
|
|
446
|
+
// ============================================
|
|
447
|
+
// Private Methods
|
|
448
|
+
// ============================================
|
|
449
|
+
ensureInitialized() {
|
|
450
|
+
if (!this.isInitialized) {
|
|
451
|
+
throw new ILLMProvider_1.LLMProviderError('OpenRouterProvider not initialized. Call initialize() first.', 'openrouter', 'NOT_INITIALIZED', false);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
buildMessages(options) {
|
|
455
|
+
return options.messages.map(m => ({
|
|
456
|
+
role: m.role,
|
|
457
|
+
content: typeof m.content === 'string'
|
|
458
|
+
? m.content
|
|
459
|
+
: m.content.filter(c => c.type === 'text').map(c => c.text || '').join(''),
|
|
460
|
+
}));
|
|
461
|
+
}
|
|
462
|
+
getHeaders() {
|
|
463
|
+
const headers = {
|
|
464
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
465
|
+
'Content-Type': 'application/json',
|
|
466
|
+
};
|
|
467
|
+
if (this.config.siteUrl) {
|
|
468
|
+
headers['HTTP-Referer'] = this.config.siteUrl;
|
|
469
|
+
}
|
|
470
|
+
if (this.config.siteName) {
|
|
471
|
+
headers['X-Title'] = this.config.siteName;
|
|
472
|
+
}
|
|
473
|
+
return headers;
|
|
474
|
+
}
|
|
475
|
+
async makeRequest(endpoint, body) {
|
|
476
|
+
let lastError = null;
|
|
477
|
+
const maxRetries = this.config.maxRetries;
|
|
478
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
479
|
+
try {
|
|
480
|
+
const response = await fetch(`${this.config.baseUrl}${endpoint}`, {
|
|
481
|
+
method: 'POST',
|
|
482
|
+
headers: this.getHeaders(),
|
|
483
|
+
body: JSON.stringify(body),
|
|
484
|
+
signal: AbortSignal.timeout(this.config.timeout),
|
|
485
|
+
});
|
|
486
|
+
if (!response.ok) {
|
|
487
|
+
const errorBody = await response.text();
|
|
488
|
+
const retryable = response.status >= 500 || response.status === 429;
|
|
489
|
+
if (retryable && attempt < maxRetries) {
|
|
490
|
+
const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
|
|
491
|
+
this.logger.warn(`Request failed (${response.status}), retrying in ${delay}ms...`);
|
|
492
|
+
await new Promise(r => setTimeout(r, delay));
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
throw new ILLMProvider_1.LLMProviderError(`OpenRouter request failed: ${response.status} - ${errorBody}`, 'openrouter', 'REQUEST_ERROR', retryable);
|
|
496
|
+
}
|
|
497
|
+
return await response.json();
|
|
498
|
+
}
|
|
499
|
+
catch (error) {
|
|
500
|
+
lastError = error;
|
|
501
|
+
if (error instanceof ILLMProvider_1.LLMProviderError && !error.retryable) {
|
|
502
|
+
throw error;
|
|
503
|
+
}
|
|
504
|
+
if (attempt < maxRetries) {
|
|
505
|
+
const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
|
|
506
|
+
this.logger.warn(`Request error, retrying in ${delay}ms...`, error);
|
|
507
|
+
await new Promise(r => setTimeout(r, delay));
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
throw lastError || new Error('Request failed after all retries');
|
|
512
|
+
}
|
|
513
|
+
mapStopReason(finishReason) {
|
|
514
|
+
switch (finishReason) {
|
|
515
|
+
case 'stop':
|
|
516
|
+
return 'end_turn';
|
|
517
|
+
case 'length':
|
|
518
|
+
return 'max_tokens';
|
|
519
|
+
case 'content_filter':
|
|
520
|
+
return 'stop_sequence';
|
|
521
|
+
default:
|
|
522
|
+
return 'end_turn';
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
handleError(error, operation) {
|
|
526
|
+
if (error instanceof ILLMProvider_1.LLMProviderError) {
|
|
527
|
+
this.logger.error(`OpenRouter ${operation} failed:`, {
|
|
528
|
+
code: error.code,
|
|
529
|
+
retryable: error.retryable,
|
|
530
|
+
message: error.message,
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
this.logger.error(`OpenRouter ${operation} error:`, error);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
exports.OpenRouterProvider = OpenRouterProvider;
|
|
539
|
+
/**
|
|
540
|
+
* Create an OpenRouter provider with configuration
|
|
541
|
+
*/
|
|
542
|
+
function createOpenRouterProvider(config) {
|
|
543
|
+
return new OpenRouterProvider(config);
|
|
544
|
+
}
|
|
545
|
+
//# sourceMappingURL=OpenRouterProvider.js.map
|