feat: OpenRouter-ready AI service — all 11 methods implemented
IAIService: + generateMatchExplanation, summarizeTradeOffs, generateDataQualitySummary, classifyMarketSignal (4 new methods covering all documented AI output types) OpenRouterAIService: - All 11 methods now make real API calls via withFallback() pattern - Every fallback is explicit (console.warn/error) — no silent mock bleed-through - Proper JSON extraction with type-safe parsers, no any casts - parseNeed: AI JSON → ParseNeedResult mapping (no TODO stubs) - generateFollowUpQuestions, generateMatchExplanation, summarizeTradeOffs, generateDataQualitySummary, classifyMarketSignal, generateOfferEmail, extractCriteria, generateFollowUp: fully implemented Prompts: +followUpQuestionsPrompt, +tradeOffPrompt, +dataQualityPrompt, +marketSignalPrompt Factory (index.ts): - VITE_AI_PROVIDER=mock|openrouter (new, takes priority) - VITE_USE_REAL_AI=true still supported (legacy compat) - Missing API key → explicit console.warn + MockAIService fallback Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -49,6 +49,62 @@ export interface ParsedListingData {
|
||||
fitOut?: string
|
||||
}
|
||||
|
||||
// ── New Response Types ────────────────────────────────────────────────────────
|
||||
|
||||
export interface MatchExplanation {
|
||||
headline: string
|
||||
summary: string
|
||||
keyReasons: string[]
|
||||
}
|
||||
|
||||
export interface TradeOffSummary {
|
||||
headline: string
|
||||
items: Array<{ concern: string; severity: 'LOW' | 'MEDIUM' | 'HIGH'; mitigation?: string }>
|
||||
overallRisk: 'LOW' | 'MEDIUM' | 'HIGH'
|
||||
}
|
||||
|
||||
export interface DataQualitySummary {
|
||||
overallAssessment: string
|
||||
missingCriticalFields: string[]
|
||||
recommendation: string
|
||||
confidence: number
|
||||
}
|
||||
|
||||
export interface MarketSignalClassification {
|
||||
signalType: 'VACANCY' | 'CONSTRUCTION' | 'RESTRUCTURING' | 'EXPANSION' | 'RELOCATION' | 'UNKNOWN'
|
||||
probability: number
|
||||
timeHorizonMonths: number | null
|
||||
areaSqmEstimate: number | null
|
||||
credibility: 'LOW' | 'MEDIUM' | 'HIGH'
|
||||
reasoning: string
|
||||
}
|
||||
|
||||
// ── Input Types ───────────────────────────────────────────────────────────────
|
||||
|
||||
export interface MatchExplanationInput {
|
||||
propertyTitle: string
|
||||
propertyCity: string
|
||||
matchScore: number
|
||||
positiveFactors: Array<{ criterion: string; explanation: string }>
|
||||
negativeFactors: Array<{ criterion: string; explanation: string }>
|
||||
needSummary: string
|
||||
}
|
||||
|
||||
export interface TradeOffInput {
|
||||
criterion: string
|
||||
concern: string
|
||||
severity: 'LOW' | 'MEDIUM' | 'HIGH'
|
||||
mitigation?: string
|
||||
}
|
||||
|
||||
export interface DataQualityInput {
|
||||
score: number
|
||||
freshness: string
|
||||
missingCriticalFields: string[]
|
||||
missingOptionalFields: string[]
|
||||
warnings: string[]
|
||||
}
|
||||
|
||||
// ── Legacy types (kept for backward compatibility) ────────────────────────────
|
||||
|
||||
export interface CriteriaExtractionResult {
|
||||
@@ -71,12 +127,22 @@ export interface IAIService {
|
||||
parseNeed(input: string): Promise<ItemResponse<ParseNeedResult>>
|
||||
generateFollowUpQuestions(criteria: ParsedNeedCriteria): Promise<ItemResponse<FollowUpQuestion[]>>
|
||||
|
||||
// Match explainability (F004)
|
||||
generateMatchExplanation(input: MatchExplanationInput): Promise<ItemResponse<MatchExplanation>>
|
||||
summarizeTradeOffs(tradeoffs: TradeOffInput[]): Promise<ItemResponse<TradeOffSummary>>
|
||||
|
||||
// Compare (F014)
|
||||
summarizeComparison(items: UnifiedMatchResult[]): Promise<ItemResponse<ComparisonSummary>>
|
||||
|
||||
// Shortlist decision brief
|
||||
generateDecisionBrief(shortlistId: string): Promise<ItemResponse<DecisionBrief>>
|
||||
|
||||
// Data quality
|
||||
generateDataQualitySummary(propertyId: string, quality: DataQualityInput): Promise<ItemResponse<DataQualitySummary>>
|
||||
|
||||
// Market signal classification (OPERATIONS)
|
||||
classifyMarketSignal(signalText: string): Promise<ItemResponse<MarketSignalClassification>>
|
||||
|
||||
// Offer email (supply side)
|
||||
generateOfferEmail(payload: OfferEmailPayload): Promise<ItemResponse<{ subject: string; body: string }>>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user