Ganesh Chintalapati commited on
Commit
89ac1a2
·
1 Parent(s): 0696c74
Files changed (1) hide show
  1. app.py +192 -38
app.py CHANGED
@@ -17,53 +17,207 @@ logger.info(f"OPENAI_API_KEY present: {'OPENAI_API_KEY' in os.environ}")
17
  logger.info(f"ANTHROPIC_API_KEY present: {'ANTHROPIC_API_KEY' in os.environ}")
18
  logger.info(f"GEMINI_API_KEY present: {'GEMINI_API_KEY' in os.environ}")
19
 
20
- # Asking functions remain the same ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
- async def query_model(query: str, providers: List[str], history: List[Dict[str, str]]) -> AsyncGenerator[List[Tuple[str, str]], None]:
23
- responses = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
- for provider in providers:
26
- provider = provider.lower()
27
- response = ""
28
- logger.info(f"Processing query with provider: {provider}")
29
- if provider == "openai":
30
- async for chunk in ask_openai(query, history):
31
- response += chunk
32
- yield [(provider, response)], history
33
- elif provider == "anthropic":
34
- response = await ask_anthropic(query, history)
35
- responses.append((provider, response))
36
- elif provider == "gemini":
37
- response = await ask_gemini(query, history)
38
- responses.append((provider, response))
39
- else:
40
- response = f"Error: Unknown provider: {provider}"
41
- logger.error(response)
42
- responses.append((provider, response))
43
-
44
- # Update history with the new query and responses
45
- updated_history = history + [{"user": query, "bot": " | ".join(resp[1] for resp in responses)}]
46
  logger.info(f"Updated history: {updated_history}")
47
- yield responses, updated_history # Final yield with updated history
48
 
49
- async def submit_query(query: str, providers: List[str], history: List[Dict[str, str]]) -> AsyncGenerator[List[str], None]:
50
  if not query.strip():
51
- yield ["Please enter a query."], history
52
  return
53
 
54
- responses = []
55
  chatbot_messages = []
56
  for msg in history:
57
  chatbot_messages.append({"role": "user", "content": msg["user"]})
58
  if msg["bot"]:
59
  chatbot_messages.append({"role": "assistant", "content": msg["bot"]})
60
-
61
- async for response_group, updated_history in query_model(query, providers, history):
62
- for provider, response in response_group:
63
- responses.append(f"{provider.capitalize()}: {response}")
64
- chatbot_messages.append({"role": "assistant", "content": f"{provider.capitalize()}: {response}"})
65
-
66
- yield responses, chatbot_messages, updated_history
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
  # Gradio interface
69
  def clear_history():
@@ -72,9 +226,9 @@ def clear_history():
72
  # Define Gradio interface
73
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
74
  gr.Markdown("# Multi-Model Chat")
75
- gr.Markdown("Chat with OpenAI, Anthropic, or Gemini. Select one or multiple providers and start typing!")
76
 
77
- provider = gr.CheckboxGroup(choices=["OpenAI", "Anthropic", "Gemini"], label="Select Provider")
78
  history_state = gr.State(value=[])
79
  chatbot = gr.Chatbot(label="Conversation", type="messages")
80
  query = gr.Textbox(label="Enter your query", placeholder="e.g., What is the capital of the United States?")
@@ -84,7 +238,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
84
  submit_button.click(
85
  fn=submit_query,
86
  inputs=[query, provider, history_state],
87
- outputs=[chatbot, history_state]
88
  )
89
  clear_button.click(
90
  fn=clear_history,
 
17
  logger.info(f"ANTHROPIC_API_KEY present: {'ANTHROPIC_API_KEY' in os.environ}")
18
  logger.info(f"GEMINI_API_KEY present: {'GEMINI_API_KEY' in os.environ}")
19
 
20
+ async def ask_openai(query: str, history: List[Dict[str, str]]) -> AsyncGenerator[str, None]:
21
+ openai_api_key = os.getenv("OPENAI_API_KEY")
22
+ if not openai_api_key:
23
+ logger.error("OpenAI API key not provided")
24
+ yield "Error: OpenAI API key not provided."
25
+ return
26
+
27
+ # Build message history
28
+ messages = []
29
+ for msg in history:
30
+ messages.append({"role": "user", "content": msg["user"]})
31
+ if msg["bot"]:
32
+ messages.append({"role": "assistant", "content": msg["bot"]})
33
+ messages.append({"role": "user", "content": query})
34
+
35
+ headers = {
36
+ "Authorization": f"Bearer {openai_api_key}",
37
+ "Content-Type": "application/json"
38
+ }
39
+
40
+ payload = {
41
+ "model": "gpt-4o-mini",
42
+ "messages": messages,
43
+ "stream": True
44
+ }
45
+
46
+ try:
47
+ async with httpx.AsyncClient() as client:
48
+ async with client.stream("POST", "https://api.openai.com/v1/chat/completions", headers=headers, json=payload) as response:
49
+ response.raise_for_status()
50
+ async for chunk in response.aiter_text():
51
+ if chunk:
52
+ # Parse the streaming chunk (JSON lines)
53
+ lines = chunk.splitlines()
54
+ for line in lines:
55
+ if line.startswith("data: "):
56
+ data = line[6:] # Remove "data: " prefix
57
+ if data == "[DONE]":
58
+ break
59
+ if not data.strip():
60
+ continue
61
+ try:
62
+ json_data = json.loads(data) # Safely parse JSON
63
+ if "choices" in json_data and json_data["choices"]:
64
+ delta = json_data["choices"][0].get("delta", {})
65
+ if "content" in delta and delta["content"] is not None:
66
+ yield delta["content"]
67
+ except json.JSONDecodeError as e:
68
+ logger.error(f"Error parsing OpenAI stream chunk: {str(e)} - Data: {data}")
69
+ yield f"Error parsing stream: {str(e)}"
70
+ except Exception as e:
71
+ logger.error(f"Unexpected error in OpenAI stream: {str(e)} - Data: {data}")
72
+ yield f"Error in stream: {str(e)}"
73
+
74
+ except httpx.HTTPStatusError as e:
75
+ # Read the response body for streaming responses
76
+ response_text = await e.response.aread()
77
+ logger.error(f"OpenAI HTTP Status Error: {e.response.status_code}, {response_text}")
78
+ yield f"Error: OpenAI HTTP Status Error: {e.response.status_code}, {response_text.decode('utf-8')}"
79
+ except Exception as e:
80
+ logger.error(f"OpenAI Error: {str(e)}")
81
+ yield f"Error: OpenAI Error: {str(e)}"
82
+
83
+ async def ask_anthropic(query: str, history: List[Dict[str, str]]) -> str:
84
+ anthropic_api_key = os.getenv("ANTHROPIC_API_KEY")
85
+ if not anthropic_api_key:
86
+ logger.error("Anthropic API key not provided")
87
+ return "Error: Anthropic API key not provided."
88
+
89
+ # Build message history
90
+ messages = []
91
+ for msg in history:
92
+ messages.append({"role": "user", "content": msg["user"]})
93
+ if msg["bot"]:
94
+ messages.append({"role": "assistant", "content": msg["bot"]})
95
+ messages.append({"role": "user", "content": query})
96
+
97
+ headers = {
98
+ "x-api-key": anthropic_api_key,
99
+ "anthropic-version": "2023-06-01",
100
+ "Content-Type": "application/json"
101
+ }
102
+
103
+ payload = {
104
+ "model": "claude-3-5-sonnet-20241022",
105
+ "max_tokens": 1024,
106
+ "messages": messages
107
+ }
108
+
109
+ try:
110
+ async with httpx.AsyncClient() as client:
111
+ logger.info(f"Sending Anthropic request: {payload}")
112
+ response = await client.post("https://api.anthropic.com/v1/messages", headers=headers, json=payload)
113
+
114
+ response.raise_for_status()
115
+ logger.info(f"Anthropic response: {response.json()}")
116
+ return response.json()['content'][0]['text']
117
+
118
+ except httpx.HTTPStatusError as e:
119
+ logger.error(f"Anthropic HTTP Status Error: {e.response.status_code}, {e.response.text}")
120
+ return f"Error: Anthropic HTTP Status Error: {e.response.status_code}, {e.response.text}"
121
+ except Exception as e:
122
+ logger.error(f"Anthropic Error: {str(e)}")
123
+ return f"Error: Anthropic Error: {str(e)}"
124
 
125
+ async def ask_gemini(query: str, history: List[Dict[str, str]]) -> str:
126
+ gemini_api_key = os.getenv("GEMINI_API_KEY")
127
+ if not gemini_api_key:
128
+ logger.error("Gemini API key not provided")
129
+ return "Error: Gemini API key not provided."
130
+
131
+ # Gemini doesn't natively support chat history in the same way, so we concatenate history as text
132
+ history_text = ""
133
+ for msg in history:
134
+ history_text += f"User: {msg['user']}\nAssistant: {msg['bot']}\n" if msg["bot"] else f"User: {msg['user']}\n"
135
+ full_query = history_text + f"User: {query}\n"
136
+
137
+ headers = {
138
+ "Content-Type": "application/json"
139
+ }
140
+
141
+ payload = {
142
+ "contents": [{"parts": [{"text": full_query}]}]
143
+ }
144
+
145
+ try:
146
+ async with httpx.AsyncClient() as client:
147
+ response = await client.post(
148
+ f"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key={gemini_api_key}",
149
+ headers=headers,
150
+ json=payload
151
+ )
152
+
153
+ response.raise_for_status()
154
+ return response.json()['candidates'][0]['content']['parts'][0]['text']
155
+
156
+ except httpx.HTTPStatusError as e:
157
+ logger.error(f"Gemini HTTP Status Error: {e.response.status_code}, {e.response.text}")
158
+ return f"Error: Gemini HTTP Status Error: {e.response.status_code}, {e.response.text}"
159
+ except Exception as e:
160
+ logger.error(f"Gemini Error: {str(e)}")
161
+ return f"Error: Gemini Error: {str(e)}"
162
+
163
+ async def query_model(query: str, provider: str, history: List[Dict[str, str]]) -> AsyncGenerator[Tuple[str, List[Dict[str, str]]], None]:
164
+ provider = provider.lower()
165
+ response = "" # Initialize response to avoid UnboundLocalError
166
 
167
+ logger.info(f"Processing query with provider: {provider}")
168
+ if provider == "openai":
169
+ async for chunk in ask_openai(query, history):
170
+ response += chunk
171
+ yield chunk, history # Yield partial response for streaming
172
+ elif provider == "anthropic":
173
+ response = await ask_anthropic(query, history)
174
+ yield response, history
175
+ elif provider == "gemini":
176
+ response = await ask_gemini(query, history)
177
+ yield response, history
178
+ else:
179
+ response = f"Error: Unknown provider: {provider}"
180
+ logger.error(response)
181
+ yield response, history
182
+
183
+ # Update history with the new query and response
184
+ updated_history = history + [{"user": query, "bot": response}]
 
 
 
185
  logger.info(f"Updated history: {updated_history}")
186
+ yield response, updated_history # Final yield with updated history
187
 
188
+ async def submit_query(query: str, provider: str, history: List[Dict[str, str]]) -> AsyncGenerator[Tuple[str, List[Dict[str, str]], List[Dict[str, str]]], None]:
189
  if not query.strip():
190
+ yield "", [{"role": "assistant", "content": "Please enter a query."}], history
191
  return
192
 
193
+ response = ""
194
  chatbot_messages = []
195
  for msg in history:
196
  chatbot_messages.append({"role": "user", "content": msg["user"]})
197
  if msg["bot"]:
198
  chatbot_messages.append({"role": "assistant", "content": msg["bot"]})
199
+ async for response_chunk, updated_history in query_model(query, provider, history):
200
+ response += response_chunk
201
+ # Update chatbot messages for streaming
202
+ chatbot_messages = []
203
+ for msg in updated_history:
204
+ chatbot_messages.append({"role": "user", "content": msg["user"]})
205
+ if msg["bot"]:
206
+ chatbot_messages.append({"role": "assistant", "content": msg["bot"]})
207
+ if response and provider == "openai":
208
+ # For streaming, show partial response
209
+ if chatbot_messages and chatbot_messages[-1]["role"] == "user":
210
+ chatbot_messages.append({"role": "assistant", "content": response})
211
+ else:
212
+ chatbot_messages[-1] = {"role": "assistant", "content": response}
213
+ yield "", chatbot_messages, updated_history # Yield to chatbot, not query
214
+ # Final yield with complete response
215
+ chatbot_messages = []
216
+ for msg in updated_history:
217
+ chatbot_messages.append({"role": "user", "content": msg["user"]})
218
+ if msg["bot"]:
219
+ chatbot_messages.append({"role": "assistant", "content": msg["bot"]})
220
+ yield "", chatbot_messages, updated_history
221
 
222
  # Gradio interface
223
  def clear_history():
 
226
  # Define Gradio interface
227
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
228
  gr.Markdown("# Multi-Model Chat")
229
+ gr.Markdown("Chat with OpenAI, Anthropic, or Gemini. Select a provider and start typing!")
230
 
231
+ provider = gr.Dropdown(choices=["OpenAI", "Anthropic", "Gemini"], label="Select Provider", value="OpenAI")
232
  history_state = gr.State(value=[])
233
  chatbot = gr.Chatbot(label="Conversation", type="messages")
234
  query = gr.Textbox(label="Enter your query", placeholder="e.g., What is the capital of the United States?")
 
238
  submit_button.click(
239
  fn=submit_query,
240
  inputs=[query, provider, history_state],
241
+ outputs=[query, chatbot, history_state]
242
  )
243
  clear_button.click(
244
  fn=clear_history,