import { GitHubIssue, GitHubIssueComment } from "./github.ts"; import { IssueLlmConfig, IssueLlmPayload, IssueLlmResponse, } from "./issue-llm-types.ts"; import { requestOpenAiCompatibleJson } from "./issue-llm-provider.ts"; import { IssueRoute, LlmAssessment, TriageMachineState, TriageResult, } from "./issue-triage-types.ts"; const ALLOWED_RISK_FLAGS = new Set([ "auth", "security", "token", "permission", "migration", "schema", "api-contract", "sdk", "cli-protocol", "data-loss", ]); const PROMPT_VERSION = 3; export async function evaluateIssueWithLlm( config: IssueLlmConfig, issue: GitHubIssue, comments: GitHubIssueComment[], ruleResult: TriageResult, previousState: TriageMachineState | null, ) { const payload = buildPayload(config, issue, comments, ruleResult); const inputHash = await buildIssueInputHash(payload); const cached = previousState?.llm; if ( cached && cached.inputHash === inputHash && cached.provider === config.provider && cached.model === config.model && cached.mode === config.mode && !cached.failed ) { return { inputHash, assessment: { ...cached, reused: true, } as LlmAssessment, }; } try { const rawJson = await requestOpenAiCompatibleJson( config, buildSystemPrompt(), JSON.stringify(payload, null, 2), ); const parsed = validateLlmResponse(JSON.parse(rawJson), ruleResult); return { inputHash, assessment: { provider: config.provider, model: config.model, mode: config.mode, inputHash, summary: parsed.summary_zh || parsed.summary || parsed.summary_en || "", summaryEn: parsed.summary_en || parsed.summary || parsed.summary_zh || "", summaryZh: parsed.summary_zh || parsed.summary || parsed.summary_en || "", impact: parsed.impact, urgency: parsed.urgency, effort: parsed.effort, confidence: parsed.confidence, riskFlags: parsed.risk_flags, missingInfo: parsed.missing_info, suggestedQuestions: parsed.suggested_questions, recommendedRoute: parsed.recommended_route, rationale: parsed.rationale, reused: false, failed: false, } satisfies LlmAssessment, }; } catch (error) { const failureReason = error instanceof Error ? error.message : String(error); return { inputHash, assessment: { provider: config.provider, model: config.model, mode: config.mode, inputHash, summary: "", summaryEn: "", summaryZh: "", impact: ruleResult.impact, urgency: ruleResult.urgency, effort: ruleResult.effort, confidence: ruleResult.confidence, riskFlags: [], missingInfo: [], suggestedQuestions: [], recommendedRoute: ruleResult.route, rationale: [], reused: false, failed: true, failureReason, } satisfies LlmAssessment, }; } } function buildPayload( config: IssueLlmConfig, issue: GitHubIssue, comments: GitHubIssueComment[], ruleResult: TriageResult, ): IssueLlmPayload { const latestComments = comments .filter((comment) => !comment.body.includes("