File size: 5,037 Bytes
faf123b
 
b44478d
 
 
cec83ba
3f95afe
7957d12
d6e70cd
faf123b
 
 
 
b44478d
 
 
 
 
 
 
 
e111889
faf123b
 
b44478d
16c61ea
cfea2d4
b44478d
 
 
 
 
 
 
 
 
 
 
 
70e1667
 
b44478d
 
ea7a6ee
 
faf123b
6215d0a
faf123b
 
 
 
 
 
cec83ba
 
 
 
 
faf123b
cec83ba
faf123b
 
 
 
 
d9d2959
 
b44478d
 
 
 
 
 
 
 
 
 
faf123b
b44478d
 
d9d2959
 
 
e111889
7957d12
 
 
 
 
faf123b
 
 
 
 
 
 
 
 
b44478d
faf123b
 
 
16c61ea
faf123b
 
165c61b
faf123b
 
 
 
 
ea7a6ee
7957d12
faf123b
 
7957d12
 
b44478d
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
from google import genai
from google.genai import types
import traceback
from ..config import db, get_user_keys

# Cap stored/replayed conversation turns so Firestore docs 
MAX_HISTORY_MESSAGES = 14

class AuditorEngine:
    @staticmethod
    def _get_client(api_key):
        return genai.Client(api_key=api_key)

    @staticmethod
    def initialize_firebase_session(uid, context):
        try:
            keys = get_user_keys(uid)
            api_key = keys.get('gemini_key')
            if not api_key: 
                return "Error: No API Key found in Settings."

            client = AuditorEngine._get_client(api_key)
            
            # Parsona
            instruction = f"""
            PERSONA:
            You are the QuantVAT AI Trading Journal Auditor, a senior Risk Manager and Trading Psychologist with 50 years trading experience like a Market Wizard. 
            Speak with veteran authority. Tone is blunt but constructive.

            MANDATE:
            1. Analyze the 'WHY' behind execution based on the provided logs.
            2. STRUCTURE: 
               - ## ๐Ÿ“Š OVERVIEW: 2-sentence performance reality check.
               - ## ๐Ÿšฉ RED FLAGS: Top 2 execution errors (FOMO, sizing, fear, etc).
               - ## ๐Ÿ’ก THE REMEDY: One specific, actionable rule for the next session.
            3. FORMAT: Use bold text for emphasis. 
            4. NO TABLES: Use bullet points only.
            5. INTERACTION: End with a provocative question about a specific trade.

            TRADING LEDGER (CSV FORMAT):
            {context}
            """

            prompt = "Analyze my execution performance based on the CSV data above. End with: 'I have analyzed your data. Ready for audit.'"
            
            response = client.models.generate_content(
                model='gemini-3.6-flash', 
                contents=prompt,
                config=types.GenerateContentConfig(
                    system_instruction=instruction
                )
            )
            
            # Preserve prior conversation turns instead of wiping them
            user_doc = db.collection('users').document(uid).get()
            prior_history = (user_doc.to_dict() or {}).get("ai_history", []) if user_doc.exists else []
            history = (prior_history + [
                {"role": "user", "parts": [{"text": prompt}]},
                {"role": "model", "parts": [{"text": response.text}]}
            ])[-MAX_HISTORY_MESSAGES:]
            
            db.collection('users').document(uid).set({
                "ai_history": history,
                "ai_context": context
            }, merge=True)
            
            return response.text
        except Exception as e:
            print(f"AI Init Error: {traceback.format_exc()}")
            return f"System Error: {str(e)}"

    @staticmethod
    def continue_firebase_chat(uid, prompt):
        try:
            user_doc = db.collection('users').document(uid).get()
            data = user_doc.to_dict() if user_doc.exists else {}
            history = data.get("ai_history", [])
            context = data.get("ai_context", "") 
            
            api_key = get_user_keys(uid).get('gemini_key')
            if not api_key: 
                return "Error: API Key missing."

            client = AuditorEngine._get_client(api_key)

            # Keep only the most recent turns when replaying to the model
            if len(history) > MAX_HISTORY_MESSAGES:
                history = history[-MAX_HISTORY_MESSAGES:]

            # Robust mapping
            contents = []
            for h in history:
                p = h['parts'][0]
                text_content = p['text'] if isinstance(p, dict) else str(p)
                contents.append(types.Content(
                    role=h['role'], 
                    parts=[types.Part.from_text(text=text_content)]
                ))
            
            contents.append(types.Content(role="user", parts=[types.Part.from_text(text=prompt)]))

            # Re-injects Persona
            instruction = f"PERSONA: QuantVAT AI Trading Journal Auditor. Senior Risk Manager.\nDATA:\n{context}"

            response = client.models.generate_content(
                model='gemini-3.6-flash',
                contents=contents,
                config=types.GenerateContentConfig(
                    system_instruction=instruction
                )
            )
            
            # Append new turn and sync to Firestore
            history.append({"role": "user", "parts": [{"text": prompt}]})
            history.append({"role": "model", "parts": [{"text": response.text}]})
            if len(history) > MAX_HISTORY_MESSAGES:
                history = history[-MAX_HISTORY_MESSAGES:]
            db.collection('users').document(uid).set({"ai_history": history}, merge=True)
            
            return response.text
        except Exception as e:
            print(f"AI Chat Error: {traceback.format_exc()}")
            return f"Auditor Error: {str(e)}"