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.
Files changed (174) hide show
  1. package/.claude/agents/qe-a11y-ally.md +855 -0
  2. package/.claude/agents/qx-partner.md +120 -4
  3. package/.claude/skills/testability-scoring/SKILL.md +107 -6
  4. package/CHANGELOG.md +135 -0
  5. package/README.md +7 -6
  6. package/dist/agents/AccessibilityAllyAgent.d.ts +168 -0
  7. package/dist/agents/AccessibilityAllyAgent.d.ts.map +1 -0
  8. package/dist/agents/AccessibilityAllyAgent.js +462 -0
  9. package/dist/agents/AccessibilityAllyAgent.js.map +1 -0
  10. package/dist/agents/SONAIntegration.d.ts +109 -0
  11. package/dist/agents/SONAIntegration.d.ts.map +1 -0
  12. package/dist/agents/SONAIntegration.js +167 -0
  13. package/dist/agents/SONAIntegration.js.map +1 -0
  14. package/dist/agents/index.d.ts +3 -0
  15. package/dist/agents/index.d.ts.map +1 -1
  16. package/dist/agents/index.js +93 -2
  17. package/dist/agents/index.js.map +1 -1
  18. package/dist/cli/init/agents.js +1 -1
  19. package/dist/cli/init/claude-config.js +2 -2
  20. package/dist/cli/init/database-init.js +1 -1
  21. package/dist/core/cache/BinaryCacheImpl.d.ts +161 -0
  22. package/dist/core/cache/BinaryCacheImpl.d.ts.map +1 -0
  23. package/dist/core/cache/BinaryCacheImpl.js +685 -0
  24. package/dist/core/cache/BinaryCacheImpl.js.map +1 -0
  25. package/dist/core/cache/BinaryMetadataCache.d.ts +244 -0
  26. package/dist/core/cache/BinaryMetadataCache.d.ts.map +1 -1
  27. package/dist/core/cache/BinaryMetadataCache.js +63 -1
  28. package/dist/core/cache/BinaryMetadataCache.js.map +1 -1
  29. package/dist/core/cache/index.d.ts +1 -0
  30. package/dist/core/cache/index.d.ts.map +1 -1
  31. package/dist/core/cache/index.js +10 -1
  32. package/dist/core/cache/index.js.map +1 -1
  33. package/dist/core/memory/AgentDBService.d.ts +30 -4
  34. package/dist/core/memory/AgentDBService.d.ts.map +1 -1
  35. package/dist/core/memory/AgentDBService.js +122 -12
  36. package/dist/core/memory/AgentDBService.js.map +1 -1
  37. package/dist/core/memory/CachedHNSWVectorMemory.d.ts +153 -0
  38. package/dist/core/memory/CachedHNSWVectorMemory.d.ts.map +1 -0
  39. package/dist/core/memory/CachedHNSWVectorMemory.js +329 -0
  40. package/dist/core/memory/CachedHNSWVectorMemory.js.map +1 -0
  41. package/dist/core/memory/HNSWVectorMemory.js +1 -1
  42. package/dist/core/memory/RuVectorPatternStore.d.ts.map +1 -1
  43. package/dist/core/memory/RuVectorPatternStore.js +8 -2
  44. package/dist/core/memory/RuVectorPatternStore.js.map +1 -1
  45. package/dist/core/memory/UnifiedMemoryCoordinator.d.ts +50 -0
  46. package/dist/core/memory/UnifiedMemoryCoordinator.d.ts.map +1 -1
  47. package/dist/core/memory/UnifiedMemoryCoordinator.js +206 -0
  48. package/dist/core/memory/UnifiedMemoryCoordinator.js.map +1 -1
  49. package/dist/core/memory/index.d.ts +2 -0
  50. package/dist/core/memory/index.d.ts.map +1 -1
  51. package/dist/core/memory/index.js +8 -1
  52. package/dist/core/memory/index.js.map +1 -1
  53. package/dist/core/optimization/RecursiveOptimizer.d.ts +233 -0
  54. package/dist/core/optimization/RecursiveOptimizer.d.ts.map +1 -0
  55. package/dist/core/optimization/RecursiveOptimizer.js +509 -0
  56. package/dist/core/optimization/RecursiveOptimizer.js.map +1 -0
  57. package/dist/core/strategies/SONALearningStrategy.d.ts +115 -0
  58. package/dist/core/strategies/SONALearningStrategy.d.ts.map +1 -0
  59. package/dist/core/strategies/SONALearningStrategy.js +656 -0
  60. package/dist/core/strategies/SONALearningStrategy.js.map +1 -0
  61. package/dist/core/strategies/TRMLearningStrategy.d.ts +162 -0
  62. package/dist/core/strategies/TRMLearningStrategy.d.ts.map +1 -0
  63. package/dist/core/strategies/TRMLearningStrategy.js +670 -0
  64. package/dist/core/strategies/TRMLearningStrategy.js.map +1 -0
  65. package/dist/core/strategies/index.d.ts +10 -1
  66. package/dist/core/strategies/index.d.ts.map +1 -1
  67. package/dist/core/strategies/index.js +4 -1
  68. package/dist/core/strategies/index.js.map +1 -1
  69. package/dist/learning/SONAFeedbackLoop.d.ts +168 -0
  70. package/dist/learning/SONAFeedbackLoop.d.ts.map +1 -0
  71. package/dist/learning/SONAFeedbackLoop.js +344 -0
  72. package/dist/learning/SONAFeedbackLoop.js.map +1 -0
  73. package/dist/learning/baselines/BaselineCollector.d.ts +1 -1
  74. package/dist/learning/baselines/BaselineCollector.js +1 -1
  75. package/dist/learning/baselines/StandardTaskSuite.d.ts +1 -1
  76. package/dist/learning/baselines/StandardTaskSuite.js +1 -1
  77. package/dist/learning/index.d.ts +2 -0
  78. package/dist/learning/index.d.ts.map +1 -1
  79. package/dist/learning/index.js +6 -1
  80. package/dist/learning/index.js.map +1 -1
  81. package/dist/mcp/server-instructions.d.ts +1 -1
  82. package/dist/mcp/server-instructions.js +1 -1
  83. package/dist/mcp/server.d.ts.map +1 -1
  84. package/dist/mcp/server.js +23 -16
  85. package/dist/mcp/server.js.map +1 -1
  86. package/dist/mcp/services/AgentRegistry.d.ts.map +1 -1
  87. package/dist/mcp/services/AgentRegistry.js +6 -1
  88. package/dist/mcp/services/AgentRegistry.js.map +1 -1
  89. package/dist/mcp/tools/qe/accessibility/accname-computation.d.ts +114 -0
  90. package/dist/mcp/tools/qe/accessibility/accname-computation.d.ts.map +1 -0
  91. package/dist/mcp/tools/qe/accessibility/accname-computation.js +566 -0
  92. package/dist/mcp/tools/qe/accessibility/accname-computation.js.map +1 -0
  93. package/dist/mcp/tools/qe/accessibility/apg-patterns.d.ts +103 -0
  94. package/dist/mcp/tools/qe/accessibility/apg-patterns.d.ts.map +1 -0
  95. package/dist/mcp/tools/qe/accessibility/apg-patterns.js +1028 -0
  96. package/dist/mcp/tools/qe/accessibility/apg-patterns.js.map +1 -0
  97. package/dist/mcp/tools/qe/accessibility/en-301-549-mapping.d.ts +48 -0
  98. package/dist/mcp/tools/qe/accessibility/en-301-549-mapping.d.ts.map +1 -0
  99. package/dist/mcp/tools/qe/accessibility/en-301-549-mapping.js +565 -0
  100. package/dist/mcp/tools/qe/accessibility/en-301-549-mapping.js.map +1 -0
  101. package/dist/mcp/tools/qe/accessibility/eu-accessibility-act.d.ts +117 -0
  102. package/dist/mcp/tools/qe/accessibility/eu-accessibility-act.d.ts.map +1 -0
  103. package/dist/mcp/tools/qe/accessibility/eu-accessibility-act.js +571 -0
  104. package/dist/mcp/tools/qe/accessibility/eu-accessibility-act.js.map +1 -0
  105. package/dist/mcp/tools/qe/accessibility/html-report-generator.d.ts +23 -0
  106. package/dist/mcp/tools/qe/accessibility/html-report-generator.d.ts.map +1 -0
  107. package/dist/mcp/tools/qe/accessibility/html-report-generator.js +1152 -0
  108. package/dist/mcp/tools/qe/accessibility/html-report-generator.js.map +1 -0
  109. package/dist/mcp/tools/qe/accessibility/index.d.ts +22 -0
  110. package/dist/mcp/tools/qe/accessibility/index.d.ts.map +1 -0
  111. package/dist/mcp/tools/qe/accessibility/index.js +38 -0
  112. package/dist/mcp/tools/qe/accessibility/index.js.map +1 -0
  113. package/dist/mcp/tools/qe/accessibility/markdown-report-generator.d.ts +18 -0
  114. package/dist/mcp/tools/qe/accessibility/markdown-report-generator.d.ts.map +1 -0
  115. package/dist/mcp/tools/qe/accessibility/markdown-report-generator.js +549 -0
  116. package/dist/mcp/tools/qe/accessibility/markdown-report-generator.js.map +1 -0
  117. package/dist/mcp/tools/qe/accessibility/remediation-code-generator.d.ts +139 -0
  118. package/dist/mcp/tools/qe/accessibility/remediation-code-generator.d.ts.map +1 -0
  119. package/dist/mcp/tools/qe/accessibility/remediation-code-generator.js +1300 -0
  120. package/dist/mcp/tools/qe/accessibility/remediation-code-generator.js.map +1 -0
  121. package/dist/mcp/tools/qe/accessibility/scan-comprehensive.d.ts +138 -0
  122. package/dist/mcp/tools/qe/accessibility/scan-comprehensive.d.ts.map +1 -0
  123. package/dist/mcp/tools/qe/accessibility/scan-comprehensive.js +1298 -0
  124. package/dist/mcp/tools/qe/accessibility/scan-comprehensive.js.map +1 -0
  125. package/dist/mcp/tools/qe/accessibility/video-vision-analyzer.d.ts +50 -0
  126. package/dist/mcp/tools/qe/accessibility/video-vision-analyzer.d.ts.map +1 -0
  127. package/dist/mcp/tools/qe/accessibility/video-vision-analyzer.js +469 -0
  128. package/dist/mcp/tools/qe/accessibility/video-vision-analyzer.js.map +1 -0
  129. package/dist/mcp/tools/qe/accessibility/webvtt-generator.d.ts +193 -0
  130. package/dist/mcp/tools/qe/accessibility/webvtt-generator.d.ts.map +1 -0
  131. package/dist/mcp/tools/qe/accessibility/webvtt-generator.js +511 -0
  132. package/dist/mcp/tools/qe/accessibility/webvtt-generator.js.map +1 -0
  133. package/dist/mcp/tools.d.ts +1 -0
  134. package/dist/mcp/tools.d.ts.map +1 -1
  135. package/dist/mcp/tools.js +61 -0
  136. package/dist/mcp/tools.js.map +1 -1
  137. package/dist/providers/HybridRouter.d.ts +34 -3
  138. package/dist/providers/HybridRouter.d.ts.map +1 -1
  139. package/dist/providers/HybridRouter.js +69 -4
  140. package/dist/providers/HybridRouter.js.map +1 -1
  141. package/dist/providers/LLMProviderFactory.d.ts +68 -1
  142. package/dist/providers/LLMProviderFactory.d.ts.map +1 -1
  143. package/dist/providers/LLMProviderFactory.js +173 -6
  144. package/dist/providers/LLMProviderFactory.js.map +1 -1
  145. package/dist/providers/OpenRouterProvider.d.ts +150 -0
  146. package/dist/providers/OpenRouterProvider.d.ts.map +1 -0
  147. package/dist/providers/OpenRouterProvider.js +545 -0
  148. package/dist/providers/OpenRouterProvider.js.map +1 -0
  149. package/dist/providers/RuvllmProvider.d.ts +130 -16
  150. package/dist/providers/RuvllmProvider.d.ts.map +1 -1
  151. package/dist/providers/RuvllmProvider.js +399 -83
  152. package/dist/providers/RuvllmProvider.js.map +1 -1
  153. package/dist/providers/index.d.ts +33 -4
  154. package/dist/providers/index.d.ts.map +1 -1
  155. package/dist/providers/index.js +72 -21
  156. package/dist/providers/index.js.map +1 -1
  157. package/dist/telemetry/instrumentation/agent.d.ts +1 -1
  158. package/dist/telemetry/instrumentation/agent.js +1 -1
  159. package/dist/telemetry/instrumentation/index.d.ts +1 -1
  160. package/dist/telemetry/instrumentation/index.js +1 -1
  161. package/dist/types/index.d.ts +2 -1
  162. package/dist/types/index.d.ts.map +1 -1
  163. package/dist/types/index.js +2 -0
  164. package/dist/types/index.js.map +1 -1
  165. package/dist/types/ruvllm.d.ts +97 -0
  166. package/dist/types/ruvllm.d.ts.map +1 -0
  167. package/dist/types/ruvllm.js +46 -0
  168. package/dist/types/ruvllm.js.map +1 -0
  169. package/dist/utils/ruvllm-loader.d.ts +94 -0
  170. package/dist/utils/ruvllm-loader.d.ts.map +1 -0
  171. package/dist/utils/ruvllm-loader.js +87 -0
  172. package/dist/utils/ruvllm-loader.js.map +1 -0
  173. package/docs/reference/agents.md +36 -1
  174. 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":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,EACrB,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EAEpB,MAAM,gBAAgB,CAAC;AAGxB;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,qDAAqD;IACrD,EAAE,EAAE,MAAM,CAAC;IACX,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qBAAqB;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,OAAO,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,2BAA2B;IAC3B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,sCAAsC;IACtC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IACzD,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,8DAA8D;IAC9D,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAgBD;;;;;GAKG;AACH,qBAAa,kBAAmB,YAAW,YAAY;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,MAAM,CAAmG;IACjH,OAAO,CAAC,aAAa,CAAU;IAC/B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,eAAe,CAA2C;gBAEtD,MAAM,GAAE,gBAAqB;IA0BzC;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAkCjC;;OAEG;YACW,cAAc;IAmC5B;;OAEG;IACG,QAAQ,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAuD7E;;OAEG;IACI,cAAc,CAAC,OAAO,EAAE,oBAAoB,GAAG,qBAAqB,CAAC,cAAc,CAAC;IA6G3F;;;OAGG;IACG,KAAK,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IA8BxE;;OAEG;IACG,WAAW,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;IAMjE;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IA0C7C;;OAEG;IACH,WAAW,IAAI,mBAAmB;IA2BlC;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAU/B;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,qBAAqB,CAAC,OAAO,CAAC,GAAG,MAAM;IAmBxD;;OAEG;IACG,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB5C;;OAEG;IACH,eAAe,IAAI,MAAM;IAIzB;;OAEG;IACG,kBAAkB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAOtD;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI;IAInD;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;OAEG;IACH,eAAe,IAAI,MAAM;IAQzB,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,UAAU;YAgBJ,WAAW;IAmDzB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,WAAW;CAWpB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,kBAAkB,CAEtF"}
@@ -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