1+ import type { Pet } from '../types/Pet.js' ;
2+ import type { JournalEntry } from '../types/JournalEntry.js' ;
3+
4+ export interface AnalysisResult {
5+ summary : string ;
6+ moodTrend : 'improving' | 'stable' | 'concerning' ;
7+ activityLevel : 'low' | 'normal' | 'high' ;
8+ healthConcerns : string [ ] ;
9+ recommendations : string [ ] ;
10+ nextCheckupSuggestion ?: string ;
11+ }
12+
13+ export class AIAnalyzer {
14+ private apiKey : string ;
15+ private baseUrl = 'https://openrouter.ai/api/v1/chat/completions' ;
16+
17+ constructor ( apiKey : string ) {
18+ this . apiKey = apiKey ;
19+ }
20+
21+ async analyzeJournalEntry ( pet : Pet , entry : JournalEntry ) : Promise < AnalysisResult > {
22+ const prompt = this . buildAnalysisPrompt ( pet , entry ) ;
23+
24+ try {
25+ const response = await fetch ( this . baseUrl , {
26+ method : 'POST' ,
27+ headers : {
28+ 'Authorization' : `Bearer ${ this . apiKey } ` ,
29+ 'Content-Type' : 'application/json' ,
30+ 'HTTP-Referer' : window . location . origin ,
31+ 'X-Title' : 'Petalytics'
32+ } ,
33+ body : JSON . stringify ( {
34+ model : 'anthropic/claude-3.5-sonnet' ,
35+ messages : [
36+ {
37+ role : 'system' ,
38+ content : 'You are a veterinary AI assistant specialized in pet health analysis. Provide helpful, accurate insights while encouraging professional veterinary care for serious concerns.'
39+ } ,
40+ {
41+ role : 'user' ,
42+ content : prompt
43+ }
44+ ] ,
45+ max_tokens : 500 ,
46+ temperature : 0.7
47+ } )
48+ } ) ;
49+
50+ if ( ! response . ok ) {
51+ throw new Error ( `API Error: ${ response . status } ` ) ;
52+ }
53+
54+ const data = await response . json ( ) ;
55+ return this . parseAnalysisResponse ( data . choices [ 0 ] . message . content ) ;
56+ } catch ( error ) {
57+ console . error ( 'AI Analysis Error:' , error ) ;
58+ throw error ;
59+ }
60+ }
61+
62+ private buildAnalysisPrompt ( pet : Pet , entry : JournalEntry ) : string {
63+ const recentEntries = pet . journalEntries
64+ ?. slice ( - 5 )
65+ . map ( ( e : JournalEntry ) => `${ e . date } : ${ e . content } ` )
66+ . join ( '\n' ) || 'No previous entries available' ;
67+
68+ return `
69+ Analyze this journal entry for ${ pet . name } , a ${ pet . age || 'unknown age' } -year-old ${ pet . breed || pet . species } :
70+
71+ LATEST ENTRY: "${ entry . content } "
72+
73+ RECENT HISTORY:
74+ ${ recentEntries }
75+
76+ PET INFO:
77+ - Breed: ${ pet . breed || pet . species }
78+ - Age: ${ pet . age || 'unknown' } years
79+ - Gender: ${ pet . gender || 'unknown' }
80+
81+ Please provide analysis in this JSON format:
82+ {
83+ "summary": "Brief summary of the entry",
84+ "moodTrend": "improving|stable|concerning",
85+ "activityLevel": "low|normal|high",
86+ "healthConcerns": ["concern1", "concern2"],
87+ "recommendations": ["rec1", "rec2"],
88+ "nextCheckupSuggestion": "When to see vet (if needed)"
89+ }
90+
91+ Consider breed-specific traits, age-related needs, and behavioral patterns. Keep recommendations practical and encouraging.` ;
92+ }
93+
94+ private parseAnalysisResponse ( content : string ) : AnalysisResult {
95+ try {
96+ // Extract JSON from response (in case there's extra text)
97+ const jsonMatch = content . match ( / \{ [ \s \S ] * \} / ) ;
98+ if ( jsonMatch ) {
99+ return JSON . parse ( jsonMatch [ 0 ] ) ;
100+ }
101+
102+ // Fallback parsing if JSON format isn't perfect
103+ return {
104+ summary : content . substring ( 0 , 100 ) + '...' ,
105+ moodTrend : 'stable' ,
106+ activityLevel : 'normal' ,
107+ healthConcerns : [ ] ,
108+ recommendations : [ 'Continue monitoring your pet\'s behavior' ] ,
109+ nextCheckupSuggestion : undefined
110+ } ;
111+ } catch ( error ) {
112+ console . error ( 'Error parsing AI response:' , error ) ;
113+ throw new Error ( 'Failed to parse AI analysis' ) ;
114+ }
115+ }
116+
117+ async testConnection ( ) : Promise < boolean > {
118+ try {
119+ const response = await fetch ( this . baseUrl , {
120+ method : 'POST' ,
121+ headers : {
122+ 'Authorization' : `Bearer ${ this . apiKey } ` ,
123+ 'Content-Type' : 'application/json' ,
124+ 'HTTP-Referer' : window . location . origin ,
125+ 'X-Title' : 'Petalytics'
126+ } ,
127+ body : JSON . stringify ( {
128+ model : 'openai/gpt-3.5-turbo' ,
129+ messages : [ { role : 'user' , content : 'test' } ] ,
130+ max_tokens : 1
131+ } )
132+ } ) ;
133+
134+ return response . ok ;
135+ } catch ( error ) {
136+ return false ;
137+ }
138+ }
139+ }
0 commit comments