heisbuba commited on
Commit
cfea2d4
·
verified ·
1 Parent(s): f069ebf

Update src/services/ai_modal_engine.py

Browse files
Files changed (1) hide show
  1. src/services/ai_modal_engine.py +25 -24
src/services/ai_modal_engine.py CHANGED
@@ -6,11 +6,14 @@ from ..config import db, get_user_keys
6
  class AiModalEngine:
7
  @staticmethod
8
  def _get_client(api_key):
9
- # Factory method for thread-safe client instantiation per request
10
  return genai.Client(api_key=api_key)
11
 
12
  @staticmethod
13
  def initialize_firebase_session(uid, context):
 
 
 
14
  try:
15
  keys = get_user_keys(uid)
16
  api_key = keys.get('gemini_key')
@@ -19,10 +22,10 @@ class AiModalEngine:
19
 
20
  client = AiModalEngine._get_client(api_key)
21
 
22
- # Context injection: CSV data is embedded in the persona constraints
23
  instruction = f"""
24
  PARSONA:
25
- You are the QuantVAT AI Trading Journal Auditor, a senior Risk Manager and Trading Psychologist.
26
  Speak with veteran authority. Tone is blunt but constructive.
27
 
28
  MANDATE:
@@ -39,10 +42,9 @@ class AiModalEngine:
39
  {context}
40
  """
41
 
42
- # Priming prompt forces the model to read the ledger immediately
43
  prompt = "Analyze my execution performance based on the CSV data above. End with: 'I have analyzed your data. Ready for audit.'"
44
 
45
- # Stateless generation call with system instruction config
46
  response = client.models.generate_content(
47
  model='gemini-3-flash-preview',
48
  contents=prompt,
@@ -51,33 +53,33 @@ class AiModalEngine:
51
  )
52
  )
53
 
54
- # Initialize history structure matching Firestore schema
55
  history = [
56
  {"role": "user", "parts": [{"text": prompt}]},
57
  {"role": "model", "parts": [{"text": response.text}]}
58
  ]
59
 
60
- # Persist history AND raw context (CSV) to survive stateless resets
61
  db.collection('users').document(uid).set({
62
  "ai_history": history,
63
- "ai_context": context
64
  }, merge=True)
65
 
66
  return response.text
67
  except Exception as e:
68
- # Catch-all for API or DB failures to prevent crash
69
  print(f"AI Init Error: {traceback.format_exc()}")
70
  return f"System Error: {str(e)}"
71
 
72
  @staticmethod
73
  def continue_firebase_chat(uid, prompt):
 
 
 
74
  try:
75
  user_doc = db.collection('users').document(uid).get()
76
  data = user_doc.to_dict() if user_doc.exists else {}
77
-
78
- # Retrieve artifacts required to rebuild the "brain"
79
  history = data.get("ai_history", [])
80
- context = data.get("ai_context", "")
81
 
82
  api_key = get_user_keys(uid).get('gemini_key')
83
  if not api_key:
@@ -85,21 +87,20 @@ class AiModalEngine:
85
 
86
  client = AiModalEngine._get_client(api_key)
87
 
88
- contents = [
89
- types.Content(
 
 
 
 
90
  role=h['role'],
91
- parts=[types.Part.from_text(text=p['parts'][0]['text'])]
92
- ) for h in history
93
- ]
94
 
95
- # Append current user prompt to the stack
96
  contents.append(types.Content(role="user", parts=[types.Part.from_text(text=prompt)]))
97
 
98
- instruction = f"""
99
- PARSONA: QuantVAT AI Trading Journal Auditor. Senior Risk Manager.
100
- ORIGINAL LEDGER CONTEXT:
101
- {context}
102
- """
103
 
104
  response = client.models.generate_content(
105
  model='gemini-3-flash-preview',
@@ -109,7 +110,7 @@ class AiModalEngine:
109
  )
110
  )
111
 
112
- # Update history with new turn and sync to DB
113
  history.append({"role": "user", "parts": [{"text": prompt}]})
114
  history.append({"role": "model", "parts": [{"text": response.text}]})
115
  db.collection('users').document(uid).set({"ai_history": history}, merge=True)
 
6
  class AiModalEngine:
7
  @staticmethod
8
  def _get_client(api_key):
9
+ """Initializes scoped GenAI client per request."""
10
  return genai.Client(api_key=api_key)
11
 
12
  @staticmethod
13
  def initialize_firebase_session(uid, context):
14
+ """
15
+ Bootstraps Auditor session using gemini-3-flash-preview.
16
+ """
17
  try:
18
  keys = get_user_keys(uid)
19
  api_key = keys.get('gemini_key')
 
22
 
23
  client = AiModalEngine._get_client(api_key)
24
 
25
+ # 100% Parity with original Persona, Mandate, and CSV injection
26
  instruction = f"""
27
  PARSONA:
28
+ You are the QuantVAT AI Trading Journal Auditor, a senior Risk Manager and Trading Psychologist with 50 years trading experience like a Market Wizard.
29
  Speak with veteran authority. Tone is blunt but constructive.
30
 
31
  MANDATE:
 
42
  {context}
43
  """
44
 
45
+ # Original priming prompt
46
  prompt = "Analyze my execution performance based on the CSV data above. End with: 'I have analyzed your data. Ready for audit.'"
47
 
 
48
  response = client.models.generate_content(
49
  model='gemini-3-flash-preview',
50
  contents=prompt,
 
53
  )
54
  )
55
 
56
+ # Persist initial state matching original history schema
57
  history = [
58
  {"role": "user", "parts": [{"text": prompt}]},
59
  {"role": "model", "parts": [{"text": response.text}]}
60
  ]
61
 
62
+ # Critical: Store ai_context to ensure 'continue' has data memory
63
  db.collection('users').document(uid).set({
64
  "ai_history": history,
65
+ "ai_context": context
66
  }, merge=True)
67
 
68
  return response.text
69
  except Exception as e:
 
70
  print(f"AI Init Error: {traceback.format_exc()}")
71
  return f"System Error: {str(e)}"
72
 
73
  @staticmethod
74
  def continue_firebase_chat(uid, prompt):
75
+ """
76
+ Resumes session using history and re-injecting the original CSV context.
77
+ """
78
  try:
79
  user_doc = db.collection('users').document(uid).get()
80
  data = user_doc.to_dict() if user_doc.exists else {}
 
 
81
  history = data.get("ai_history", [])
82
+ context = data.get("ai_context", "") # Retrieve the original CSV
83
 
84
  api_key = get_user_keys(uid).get('gemini_key')
85
  if not api_key:
 
87
 
88
  client = AiModalEngine._get_client(api_key)
89
 
90
+ # Robust mapping: handles both old string parts and new dict parts
91
+ contents = []
92
+ for h in history:
93
+ p = h['parts'][0]
94
+ text_content = p['text'] if isinstance(p, dict) else str(p)
95
+ contents.append(types.Content(
96
  role=h['role'],
97
+ parts=[types.Part.from_text(text=text_content)]
98
+ ))
 
99
 
 
100
  contents.append(types.Content(role="user", parts=[types.Part.from_text(text=prompt)]))
101
 
102
+ # Re-injects Persona + CSV to prevent memory loss in follow-ups
103
+ instruction = f"PARSONA: QuantVAT AI Trading Journal Auditor. Senior Risk Manager.\nDATA:\n{context}"
 
 
 
104
 
105
  response = client.models.generate_content(
106
  model='gemini-3-flash-preview',
 
110
  )
111
  )
112
 
113
+ # Append new turn and sync to Firestore
114
  history.append({"role": "user", "parts": [{"text": prompt}]})
115
  history.append({"role": "model", "parts": [{"text": response.text}]})
116
  db.collection('users').document(uid).set({"ai_history": history}, merge=True)