File size: 7,074 Bytes
85ff768
 
 
 
 
 
 
 
814245b
85ff768
814245b
85ff768
 
 
 
 
 
 
 
 
 
814245b
85ff768
 
 
 
 
 
 
 
814245b
85ff768
 
 
 
 
 
 
814245b
85ff768
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
814245b
e4ec6e0
814245b
 
85ff768
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
"""

Gemini AI Client for Chat Search

Uses Gemini 1.5 Flash to summarize search results and answer questions.

"""

import os
from typing import List, Dict, Optional

# Try importing Google GenAI (new package)
try:
    from google import genai
    HAS_GEMINI = True
except ImportError:
    HAS_GEMINI = False


class GeminiClient:
    """Client for Gemini AI API."""

    def __init__(self, api_key: Optional[str] = None):
        self.api_key = api_key or os.environ.get('GEMINI_API_KEY')
        self.client = None
        self._initialized = False

    def _initialize(self):
        """Initialize the Gemini client."""
        if self._initialized:
            return True

        if not HAS_GEMINI:
            print("google-genai not installed")
            return False

        if not self.api_key:
            print("GEMINI_API_KEY not set")
            return False

        try:
            self.client = genai.Client(api_key=self.api_key)
            self._initialized = True
            print("Gemini client initialized")
            return True
        except Exception as e:
            print(f"Failed to initialize Gemini: {e}")
            return False

    def answer_from_context(self, query: str, search_results: List[Dict],

                           max_results: int = 5) -> Dict:
        """

        Generate an answer based on search results.



        Args:

            query: User's question

            search_results: List of search results with context

            max_results: Max results to include in context



        Returns:

            Dict with 'answer', 'sources', and 'success'

        """
        if not self._initialize():
            return {
                'success': False,
                'error': 'Gemini not available',
                'answer': None
            }

        # Build context from search results
        context_parts = []
        sources = []

        for i, result in enumerate(search_results[:max_results]):
            # Handle different result formats
            if 'message' in result:
                # search_with_context format
                msg = result['message']
                context_parts.append(f"""

--- 转讜爪讗讛 {i+1} (爪讬讜谉: {result.get('score', 0):.2f}) ---

诪讗转: {msg.get('from_name', '诇讗 讬讚讜注')}

转讗专讬讱: {msg.get('date', '诇讗 讬讚讜注')}

讛讜讚注讛: {msg.get('text', '')}

""")
                sources.append({
                    'from_name': msg.get('from_name'),
                    'date': msg.get('date'),
                    'message_id': result.get('message_id')
                })

                # Add context if available
                if result.get('context_before'):
                    context_parts.append("讛拽砖专 诇驻谞讬:")
                    for ctx in result['context_before']:
                        context_parts.append(f"  [{ctx.get('from_name', '?')}] {ctx.get('text_plain', '')[:100]}")

                if result.get('context_after'):
                    context_parts.append("讛拽砖专 讗讞专讬:")
                    for ctx in result['context_after']:
                        context_parts.append(f"  [{ctx.get('from_name', '?')}] {ctx.get('text_plain', '')[:100]}")

            elif 'chunk_text' in result:
                # hybrid_search format
                context_parts.append(f"""

--- 转讜爪讗讛 {i+1} (爪讬讜谉: {result.get('score', 0):.2f}) ---

{result.get('chunk_text', '')}

""")
                sources.append({
                    'message_id': result.get('message_id'),
                    'score': result.get('score')
                })

        context = "\n".join(context_parts)

        # Build prompt
        prompt = f"""讗转讛 注讜讝专 砖诪谞转讞 砖讬讞讜转 诪拽讘讜爪转 讟诇讙专诐 讜注讜谞讛 注诇 砖讗诇讜转.



讛砖讗诇讛: {query}



诇讛诇谉 转讜爪讗讜转 讞讬驻讜砖 专诇讜讜谞讟讬讜转 诪讛砖讬讞讜转:



{context}



讛谞讞讬讜转:

1. 注谞讛 讘注讘专讬转

2. 转谉 转砖讜讘讛 拽爪专讛 讜诪诪讜拽讚转 (1-3 诪砖驻讟讬诐)

3. 讗诐 讛诪讬讚注 诇讗 讘专讜专 讗讜 诇讗 拽讬讬诐 讘转讜爪讗讜转, 讗诪讜专 "诇讗 诪爪讗转讬 诪讬讚注 讘专讜专"

4. 爪讬讬谉 讗转 讛诪拽讜专 (砖诐 讛砖讜诇讞 讜讛转讗专讬讱) 讗诐 专诇讜讜谞讟讬

5. 讗诇 转诪爪讬讗 诪讬讚注 砖诇讗 诪讜驻讬注 讘转讜爪讗讜转



讛转砖讜讘讛:"""

        try:
            response = self.client.models.generate_content(
                model='gemini-2.5-flash',
                contents=prompt
            )
            answer = response.text.strip()

            return {
                'success': True,
                'answer': answer,
                'sources': sources,
                'query': query,
                'results_used': len(context_parts)
            }

        except Exception as e:
            return {
                'success': False,
                'error': str(e),
                'answer': None
            }

    def is_available(self) -> bool:
        """Check if Gemini is available."""
        return self._initialize()


# Singleton instance
_gemini_client = None


def get_gemini_client() -> GeminiClient:
    """Get or create Gemini client instance."""
    global _gemini_client
    if _gemini_client is None:
        _gemini_client = GeminiClient()
    return _gemini_client


def ai_search(query: str, limit: int = 5) -> Dict:
    """

    Perform AI-powered search: hybrid search + Gemini summarization.



    Args:

        query: Search query

        limit: Max results to use



    Returns:

        Dict with answer and metadata

    """
    from hybrid_search import get_hybrid_search

    # Get hybrid search results
    hs = get_hybrid_search()
    results = hs.search_with_context(query, limit=limit)

    if not results:
        return {
            'success': False,
            'error': 'No search results found',
            'answer': '诇讗 谞诪爪讗讜 转讜爪讗讜转 诇讞讬驻讜砖 讝讛',
            'query': query
        }

    # Get AI answer
    client = get_gemini_client()
    response = client.answer_from_context(query, results, max_results=limit)

    # Add raw results for transparency
    response['search_results'] = results

    return response


# CLI for testing
if __name__ == '__main__':
    import sys

    if len(sys.argv) < 2:
        print("Usage: python gemini_client.py 'search query'")
        print("\nChecking Gemini availability...")
        client = get_gemini_client()
        if client.is_available():
            print("Gemini is available!")
        else:
            print("Gemini is NOT available. Set GEMINI_API_KEY environment variable.")
        sys.exit(0)

    query = ' '.join(sys.argv[1:])
    print(f"\n=== AI Search: {query} ===\n")

    result = ai_search(query)

    if result['success']:
        print(f"Answer: {result['answer']}")
        print(f"\nSources: {len(result.get('sources', []))} results used")
    else:
        print(f"Error: {result.get('error')}")