( systemPrompt: string, userPrompt: string, llmContext: LlmCallContext, logger?: LoggerLike, maxContentLength: number = DEFAULT_MAX_SEMANTIC_INPUT_CHARS, )
| 278 | } |
| 279 | |
| 280 | async function callSemanticDetectionApi( |
| 281 | systemPrompt: string, |
| 282 | userPrompt: string, |
| 283 | llmContext: LlmCallContext, |
| 284 | logger?: LoggerLike, |
| 285 | maxContentLength: number = DEFAULT_MAX_SEMANTIC_INPUT_CHARS, |
| 286 | ): Promise<Record<string, unknown> | null> { |
| 287 | |
| 288 | try { |
| 289 | const controller = new AbortController(); |
| 290 | const timeoutId = setTimeout(() => controller.abort(), SEMANTIC_TIMEOUT_MS); |
| 291 | |
| 292 | const result = await completeSimple( |
| 293 | llmContext.model, |
| 294 | { |
| 295 | systemPrompt, |
| 296 | messages: [ |
| 297 | { |
| 298 | role: "user", |
| 299 | content: userPrompt, |
| 300 | timestamp: Date.now(), |
| 301 | }, |
| 302 | ], |
| 303 | } as Context, |
| 304 | { |
| 305 | ...llmContext.options, |
| 306 | temperature: 0, |
| 307 | }, |
| 308 | ); |
| 309 | |
| 310 | clearTimeout(timeoutId); |
| 311 | |
| 312 | const textContent = result.content.find((c) => c.type === "text"); |
| 313 | const content = textContent ? textContent.text : null; |
| 314 | |
| 315 | if (!content) { |
| 316 | logger?.error(`[FoundationScan] Semantic detection call warning: empty response content`); |
| 317 | return null; |
| 318 | } |
| 319 | |
| 320 | return extractJsonObject(content); |
| 321 | } catch (err) { |
| 322 | const errorMessage = err instanceof Error ? err.message : String(err); |
| 323 | logger?.error(`[FoundationScan] Semantic detection call failed: ${errorMessage}`); |
| 324 | return null; |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | function extractJsonObject(text: string): Record<string, unknown> | null { |
| 329 | const trimmed = text.trim(); |
no test coverage detected