Divyanshu3321 commited on
Commit
2aa4095
·
1 Parent(s): c0a675e

Intial Code

Browse files
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ .env
2
+ .venv
3
+ __pycache__
gemini_history.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from google.genai.types import Part, Content, UserContent
2
+
3
+ class ConversationBuilder:
4
+ def build_with_user_content(self, user_text: str):
5
+ response = [
6
+ UserContent(parts=[Part(text=user_text)]), # will get a role='user' automatically
7
+ ]
8
+ return response
9
+
10
+ def build_with_content_only(self, model_text: str):
11
+ response = [
12
+ Content(parts=[Part(text=model_text)], role='model')
13
+ ]
14
+ return response
15
+
16
+ def build_combined(self, user_text: str, model_text: str):
17
+ response = [
18
+ UserContent(parts=[Part(text=user_text)]),
19
+ Content(parts=[Part(text=model_text)], role='model')
20
+ ]
21
+ return response
gemini_llm.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from google import genai
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+
7
+ class GeminiLLM:
8
+ def __init__(self, api_key=None):
9
+ self.api_key = api_key or os.environ.get("GEMINI_API_Key")
10
+ if not self.api_key:
11
+ raise ValueError("API key must be provided or set in the environment variable 'GEMINI_API_Key'")
12
+ try:
13
+ self.client = genai.Client(api_key=self.api_key)
14
+ except Exception as e:
15
+ raise RuntimeError(f"Failed to initialize Gemini client: {str(e)}")
16
+ self.model_id = "gemini-2.0-flash"
17
+
18
+ def generate_response(self, prompt: str) -> str:
19
+ try:
20
+ response = self.client.models.generate_content(
21
+ model=self.model_id,
22
+ contents=prompt,
23
+ )
24
+ if hasattr(response, "text"):
25
+ return response.text
26
+ raise RuntimeError("No response text received from model")
27
+ except Exception as e:
28
+ raise RuntimeError(f"Error generating response: {str(e)}")
29
+
30
+ def stream_response(self, prompt: str):
31
+ try:
32
+ stream = self.client.models.generate_content_stream(
33
+ model=self.model_id,
34
+ contents=prompt,
35
+ )
36
+ for chunk in stream:
37
+ if hasattr(chunk, "text") and chunk.text:
38
+ yield chunk.text
39
+ except Exception as e:
40
+ yield f"Error during streaming: {str(e)}"
gemini_tool_selector.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from google import genai
3
+ from google.genai import types
4
+ from gemini_tools import GeminiCodeExecutionTool, GeminiThinking, GoogleSearchTool
5
+ from utils import get_typing_indicator_html
6
+
7
+ class GeminiToolSelector():
8
+ def __init__(self) -> None:
9
+ pass
10
+
11
+ def handle_tool_response(self, tool_name, tool_args, geminillm):
12
+ if tool_name == "GeminiThinking":
13
+ gen = GeminiThinking(geminillm).activate_stream(tool_args['query'])
14
+ typing_placeholder = st.empty()
15
+ thought_placeholder = st.empty()
16
+ response_placeholder = st.empty()
17
+
18
+ thought_buffer = ""
19
+ streamed_output = ""
20
+
21
+ typing_placeholder.markdown(get_typing_indicator_html("Thinking"), unsafe_allow_html=True)
22
+
23
+ for chunk in gen:
24
+ if chunk["thought"]:
25
+ thought_buffer += chunk["text"] + "\n"
26
+ with thought_placeholder.expander("Thoughts (click to expand)", expanded=False):
27
+ st.markdown(thought_buffer)
28
+ else:
29
+ break
30
+
31
+ typing_placeholder.empty()
32
+
33
+ if chunk and not chunk["thought"]:
34
+ streamed_output += chunk["text"]
35
+ response_placeholder.markdown(streamed_output)
36
+
37
+ for chunk in gen:
38
+ if not chunk["thought"]:
39
+ streamed_output += chunk["text"]
40
+ response_placeholder.markdown(streamed_output)
41
+
42
+ return streamed_output
43
+
44
+ elif tool_name == "GoogleSearchTool":
45
+ gen = GoogleSearchTool(geminillm).activate_stream(tool_args['query'])
46
+ typing_placeholder = st.empty()
47
+ typing_placeholder.markdown(get_typing_indicator_html("Searching Web"), unsafe_allow_html=True)
48
+ response_placeholder = st.empty()
49
+
50
+ try:
51
+ first_chunk = next(gen)
52
+ except StopIteration:
53
+ first_chunk = ""
54
+
55
+ typing_placeholder.empty()
56
+ streamed_output = first_chunk
57
+ response_placeholder.markdown(streamed_output)
58
+
59
+ for chunk in gen:
60
+ streamed_output += chunk
61
+ response_placeholder.markdown(streamed_output)
62
+
63
+ return streamed_output
64
+
65
+ elif tool_name == "GeminiCodeExecutionTool":
66
+ gen = GeminiCodeExecutionTool(geminillm).activate_stream(tool_args['query'])
67
+ typing_placeholder = st.empty()
68
+ typing_placeholder.markdown(get_typing_indicator_html("Writing Code"), unsafe_allow_html=True)
69
+ response_placeholder = st.empty()
70
+
71
+ try:
72
+ first_chunk = next(gen)
73
+ except StopIteration:
74
+ first_chunk = ""
75
+
76
+ typing_placeholder.empty()
77
+ streamed_output = first_chunk
78
+ response_placeholder.markdown(streamed_output)
79
+
80
+ for chunk in gen:
81
+ streamed_output += chunk
82
+ response_placeholder.markdown(streamed_output)
83
+
84
+ return streamed_output
85
+
86
+ else:
87
+ return "Unknown tool triggered."
gemini_tools.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from google.genai import types
2
+ from google.genai.types import Tool, GenerateContentConfig, GoogleSearch
3
+ from gemini_llm import GeminiLLM
4
+ from bs4 import BeautifulSoup
5
+ import requests
6
+
7
+ class GeminiThinking:
8
+ def __init__(self, geminillm: GeminiLLM, model_id="gemini-2.5-flash-preview-05-20"):
9
+ self.geminillm = geminillm
10
+ self.modelid = model_id
11
+
12
+ def activate_stream(self, prompt: str):
13
+ try:
14
+ stream = self.geminillm.client.models.generate_content_stream(
15
+ model=self.modelid,
16
+ contents=prompt,
17
+ config=types.GenerateContentConfig(
18
+ thinking_config=types.ThinkingConfig(include_thoughts=True)
19
+ )
20
+ )
21
+
22
+ for chunk in stream:
23
+ if not chunk.candidates:
24
+ continue
25
+
26
+ for part in chunk.candidates[0].content.parts:
27
+ if hasattr(part, "thought") and part.thought:
28
+ yield {"text": part.text, "thought": True}
29
+ elif hasattr(part, "text") and part.text:
30
+ yield {"text": part.text, "thought": False}
31
+
32
+ except Exception as e:
33
+ yield {"text": f"Error during GeminiThinking stream: {str(e)}", "thought": False}
34
+
35
+
36
+ class GoogleSearchTool:
37
+ def __init__(self, geminillm: GeminiLLM):
38
+ self.geminillm = geminillm
39
+ self.tool = Tool(google_search=GoogleSearch())
40
+ self.response = None
41
+ self.grounding = None
42
+
43
+ def activate_stream(self, prompt: str):
44
+ try:
45
+ stream = self.geminillm.client.models.generate_content_stream(
46
+ model=self.geminillm.model_id,
47
+ contents=prompt,
48
+ config=types.GenerateContentConfig(
49
+ tools=[self.tool],
50
+ response_modalities=["TEXT"]
51
+ )
52
+ )
53
+
54
+ for chunk in stream:
55
+ if not chunk.candidates:
56
+ continue
57
+
58
+ candidate = chunk.candidates[0]
59
+ self.grounding = candidate.grounding_metadata if hasattr(candidate, "grounding_metadata") else None
60
+
61
+ for part in candidate.content.parts:
62
+ if hasattr(part, "text") and part.text:
63
+ yield part.text
64
+
65
+ except Exception as e:
66
+ yield f"Error during GoogleSearchTool stream: {str(e)}"
67
+
68
+ class GeminiCodeExecutionTool:
69
+ def __init__(self, geminillm: GeminiLLM):
70
+ self.geminillm = geminillm
71
+
72
+ def activate_stream(self, prompt: str):
73
+ try:
74
+ stream = self.geminillm.client.models.generate_content_stream(
75
+ model=self.geminillm.model_id,
76
+ contents=prompt,
77
+ config=types.GenerateContentConfig(
78
+ tools=[types.Tool(code_execution=types.ToolCodeExecution())],
79
+ response_modalities=["TEXT"]
80
+ ),
81
+ )
82
+
83
+ for chunk in stream:
84
+ if not chunk.candidates:
85
+ continue
86
+
87
+ candidate = chunk.candidates[0]
88
+ for part in candidate.content.parts:
89
+ if hasattr(part, "text") and part.text:
90
+ yield part.text
91
+ elif hasattr(part, "executable_code") and part.executable_code:
92
+ yield f"\n\n```python\n{part.executable_code.code}\n```\n"
93
+ elif hasattr(part, "code_execution_result") and part.code_execution_result:
94
+ yield f"\n\n**Output:**\n```\n{part.code_execution_result.output}\n```\n"
95
+
96
+ except Exception as e:
97
+ yield f"Error during code execution: {str(e)}"
98
+
99
+
100
+
101
+
gemini_tools_defination.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from google.genai import types
2
+ from google import genai
3
+ from gemini_llm import GeminiLLM
4
+
5
+ class GeminiToolDefination:
6
+ def __init__(self, geminillm: GeminiLLM, result=None):
7
+ self.geminillm = geminillm
8
+ self.result = result
9
+
10
+ self.gemini_thinking_declaration = {
11
+ "name": "GeminiThinking",
12
+ "description": (
13
+ "Use this tool when the user's input suggests they are looking for thoughtful reflection, brainstorming, "
14
+ "hypothetical reasoning, or deep analysis. This includes philosophical questions, complex scenarios, or tasks "
15
+ "where the agent must 'think' or 'reflect' to provide a structured or creative response. "
16
+ "When to use: if the user is asking 'what if', 'analyze', 'brainstorm', or 'explore possibilities'. "
17
+ "Avoid this tool for factual, time-sensitive, or technical queries—use search or code tools instead."
18
+ ),
19
+ "parameters": {
20
+ "type": "object",
21
+ "properties": {
22
+ "query": {
23
+ "type": "string",
24
+ "description": (
25
+ "Queries asking for reasoning or deep thought. "
26
+ "Examples: 'What are some possible futures if humans colonize Mars?', "
27
+ "'Can you explore the societal effects of AI on human creativity?', "
28
+ "'Is ambition more helpful or harmful in life?'"
29
+ )
30
+ }
31
+ },
32
+ "required": ["query"]
33
+ }
34
+ }
35
+
36
+ self.google_search_tool = {
37
+ "name": "GoogleSearchTool",
38
+ "description": (
39
+ "Use this tool when the user is asking for specific, real-time, or factual information that may be outside the model's knowledge. "
40
+ "This includes recent news, live events, product prices, names, or anything the model cannot confidently answer from training data alone. "
41
+ "When to use: if the model would otherwise respond with 'I'm not sure', 'As of my last update', or 'I cannot browse the web'. "
42
+ "Avoid this for reasoning or general knowledge that doesn't need live updates."
43
+ ),
44
+ "parameters": {
45
+ "type": "object",
46
+ "properties": {
47
+ "query": {
48
+ "type": "string",
49
+ "description": (
50
+ "Use for fact-seeking or current-event queries. "
51
+ "Examples: 'What is the weather in Delhi today?', 'Latest iPhone 16 release date in India', "
52
+ "'Who is the current CEO of Google?', 'How did the 2024 elections end?'"
53
+ )
54
+ }
55
+ },
56
+ "required": ["query"]
57
+ }
58
+ }
59
+
60
+ self.gemini_code_execution_tool = {
61
+ "name": "GeminiCodeExecutionTool",
62
+ "description": (
63
+ "Use this tool when the user's prompt involves programming—writing, debugging, explaining, or executing code. "
64
+ "Only invoke this for well-scoped technical/code tasks that require functional accuracy or output simulation. "
65
+ "When to use: if the user asks for code generation, debugging, implementation, or syntax correction. "
66
+ "Avoid this for general tech explanations—those don't require execution."
67
+ ),
68
+ "parameters": {
69
+ "type": "object",
70
+ "properties": {
71
+ "query": {
72
+ "type": "string",
73
+ "description": (
74
+ "Programming-related prompts. "
75
+ "Examples: 'Write a Python function to reverse a list', 'Fix this error in my code: ...', "
76
+ "'What will this JavaScript function return?', 'Create a REST API in Flask'."
77
+ )
78
+ }
79
+ },
80
+ "required": ["query"]
81
+ }
82
+ }
83
+
84
+ self.tools = types.Tool(function_declarations=[
85
+ self.gemini_thinking_declaration,
86
+ self.google_search_tool,
87
+ self.gemini_code_execution_tool
88
+ ])
89
+
90
+ self.config = types.GenerateContentConfig(tools=[self.tools])
91
+
92
+ def pick_the_tool(self, prompt_text: str):
93
+ contents = [
94
+ types.Content(
95
+ role="user",
96
+ parts=[types.Part(text=prompt_text)]
97
+ )
98
+ ]
99
+
100
+ try:
101
+ response = self.geminillm.client.models.generate_content(
102
+ model=self.geminillm.model_id,
103
+ config=self.config,
104
+ contents=contents
105
+ )
106
+ self.result = response
107
+ return self.result
108
+ except Exception as e:
109
+ raise RuntimeError(f"Failed to generate response with tools: {str(e)}")
images/ai.png ADDED
images/person_15454011.png ADDED
requirements.txt CHANGED
Binary files a/requirements.txt and b/requirements.txt differ
 
src/streamlit_app.py CHANGED
@@ -1,40 +1,110 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
+ from google import genai
3
+ from google.genai import types
4
+ from gemini_tools_defination import GeminiToolDefination
5
+ from gemini_llm import GeminiLLM
6
+ from gemini_history import ConversationBuilder
7
+ from gemini_tools import GeminiCodeExecutionTool, GeminiThinking, GoogleSearchTool
8
+ from utils import get_typing_indicator_html
9
+ from gemini_tool_selector import GeminiToolSelector
10
 
11
+ geminillm=GeminiLLM()
12
+ geminitooldefination = GeminiToolDefination(geminillm)
13
+ conversationbuilder = ConversationBuilder()
14
+ geminitoolselector = GeminiToolSelector()
15
+ client = geminillm.client
16
+
17
+ if "chat" not in st.session_state:
18
+ st.session_state.chat = client.chats.create(model=geminillm.model_id, config=geminitooldefination.config)
19
+
20
+ if "messages" not in st.session_state:
21
+ st.session_state.messages = []
22
+
23
+ # default message
24
+ if not st.session_state.messages:
25
+ with st.chat_message("assistant", avatar="images/ai.png"):
26
+ st.markdown("👋 **Hi! I can help you think, code, or search real-time info. Just ask!**")
27
+
28
+ # Tool name map
29
+ tool_mapping = {
30
+ "Auto": None,
31
+ "Think": "GeminiThinking",
32
+ "Search": "GoogleSearchTool",
33
+ "Code": "GeminiCodeExecutionTool"
34
+ }
35
+
36
+ # display chat messages from history at every rerun
37
+ for message in st.session_state.messages:
38
+ avatar_path = (
39
+ "images/person_15454011.png" if message["role"] == "user"
40
+ else "images/ai.png"
41
+ )
42
+ with st.chat_message(message["role"], avatar=avatar_path):
43
+ st.markdown(message["content"])
44
+
45
+ if "selected_tool" not in st.session_state:
46
+ st.session_state.selected_tool = "Auto"
47
+
48
+ st.sidebar.title("Tools")
49
+ selected_tool = st.sidebar.selectbox(
50
+ "Choose a tool:",
51
+ options=list(tool_mapping.keys()),
52
+ index=list(tool_mapping.keys()).index(st.session_state.selected_tool),
53
+ )
54
+
55
+ # Update the session state with current selection
56
+ st.session_state.selected_tool = selected_tool
57
+
58
+ prompt = st.chat_input("What is up?")
59
+ selected_tool_name = tool_mapping[st.session_state.selected_tool]
60
+
61
+
62
+ if prompt:
63
+ try:
64
+ # show user message
65
+ with st.chat_message("user", avatar="images/person_15454011.png"):
66
+ st.markdown(prompt)
67
+ # Save user message
68
+ st.session_state.messages.append({"role": "user", "content": prompt})
69
+
70
+ # stream assistant response
71
+ full_response = ""
72
+ with st.chat_message("assistant", avatar="images/ai.png"):
73
+ response_stream = st.session_state.chat.send_message_stream(prompt)
74
+ response_placeholder = st.empty()
75
+
76
+ if selected_tool_name:
77
+ tool_args = {"query": prompt}
78
+ full_response = geminitoolselector.handle_tool_response(selected_tool_name, tool_args, geminillm)
79
+ # need to add response in chat history
80
+ content_only_convo = conversationbuilder.build_with_content_only(full_response)
81
+ st.session_state.chat.get_history().extend(content_only_convo)
82
+
83
+ else:
84
+ for chunk in response_stream:
85
+ if chunk.text is None: # if response is None means a tool is hit
86
+ tool_name = chunk.candidates[0].content.parts[0].function_call.name
87
+ tool_args = chunk.candidates[0].content.parts[0].function_call.args
88
+
89
+ response_placeholder = st.empty()
90
+
91
+ # handle tool response
92
+ streamed_output = geminitoolselector.handle_tool_response(tool_name, tool_args, geminillm)
93
+
94
+ full_response = streamed_output
95
+
96
+ # IMP..... add the response in the chathistory can see by st.session_state.chat.get_history() see complete history you will see the content is missing there and u can also more then one conttent
97
+ content_only_convo = conversationbuilder.build_with_content_only(full_response)
98
+ st.session_state.chat.get_history().extend(content_only_convo)
99
+
100
+ # st.markdown(st.session_state.chat.get_history()) ## for reference how chat history is working
101
+ else:
102
+ full_response += chunk.text
103
+ response_placeholder.markdown(full_response + "") # stream effect
104
+
105
+ # save assistant message (load all the messages to show chat type view in UI )
106
+ st.session_state.messages.append({"role": "assistant", "content": full_response})
107
+ # print(st.session_state.chat.get_history()) ## for reference how chat history is working
108
+
109
+ except Exception as e:
110
+ st.error(f"An error occurred: {e}")
system_prompt.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ system_prompt = """
2
+ You are an intelligent assistant integrated with multiple reasoning and execution tools. Your primary task is to analyze the user's intent and route their query to the most appropriate internal tool for accurate and efficient assistance.
3
+
4
+ Available Tools:
5
+
6
+ 1. GeminiThinking (GeminiThinking)
7
+ - Use this when the user is seeking thoughtful reflection, brainstorming, speculative reasoning, or deeper philosophical analysis.
8
+ - Examples:
9
+ - "What if humans could upload consciousness?"
10
+ - "Brainstorm startup ideas using AI in agriculture"
11
+ - "Is ambition more harmful or helpful in life?"
12
+
13
+ 2. Google Search Tool (GoogleSearchTool)
14
+ - Use this when the user asks for real-time, fact-based, or current information beyond your knowledge cut-off.
15
+ - Examples:
16
+ - "What’s the weather in Delhi today?"
17
+ - "When is the next iPhone launching in India?"
18
+ - "Who won the 2024 Lok Sabha elections?"
19
+
20
+ 3. Gemini Code Execution Tool (GeminiCodeExecutionTool)
21
+ - Use this when the user asks for programming help such as code generation, debugging, or technical implementations.
22
+ - Examples:
23
+ - "Write Python code to find prime numbers"
24
+ - "Fix this error: IndexError in my loop"
25
+ - "Explain how to use decorators in Python"
26
+
27
+ Response Guidelines:
28
+
29
+ - Format responses in Markdown for clarity.
30
+ - Provide a clear, structured answer before invoking any tool.
31
+ - Be concise, respectful, and professional in tone.
32
+ - If necessary data for a tool is missing (e.g., vague question or unclear intent), ask the user for clarification before invoking any tool.
33
+
34
+ Do NOT invoke any tool if:
35
+
36
+ - The user's query is off-topic, conversational, or requires general advice not requiring execution or deep reasoning.
37
+ - The query is too vague or lacks clarity. Ask for more information first.
38
+
39
+ Examples:
40
+
41
+ - "Tell me about AI impact on jobs in future" → Use (GeminiThinking)
42
+ - "What's the price of Bitcoin now?" → Use (GoogleSearchTool)
43
+ - "Create a Flask API for user login" → Use (GeminiCodeExecutionTool)
44
+ - "What is recursion?" → Do NOT invoke any tool. Just explain it.
45
+
46
+ Only invoke a tool if the user's intent clearly aligns with a tool’s purpose and all needed inputs are available.
47
+ """
utils.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def get_typing_indicator_html(label_text="Thinking"):
2
+ return f"""
3
+ <div class="thinking-wrap">
4
+ <span class="label">{label_text}</span>
5
+ <span class="dot dot1"></span>
6
+ <span class="dot dot2"></span>
7
+ <span class="dot dot3"></span>
8
+ </div>
9
+
10
+ <style>
11
+ .thinking-wrap {{
12
+ font-family: 'Arial', sans-serif;
13
+ color: #666;
14
+ font-size: 16px;
15
+ display: flex;
16
+ align-items: center;
17
+ gap: 4px;
18
+ padding: 6px 0;
19
+ }}
20
+ .label {{
21
+ font-weight: bold;
22
+ }}
23
+ .dot {{
24
+ width: 6px;
25
+ height: 6px;
26
+ background: #666;
27
+ border-radius: 50%;
28
+ animation: blink 1s infinite ease-in-out;
29
+ }}
30
+ .dot2 {{ animation-delay: 0.2s; }}
31
+ .dot3 {{ animation-delay: 0.4s; }}
32
+
33
+ @keyframes blink {{
34
+ 0%, 100% {{ opacity: 0.2; transform: translateY(0); }}
35
+ 50% {{ opacity: 1; transform: translateY(-4px); }}
36
+ }}
37
+ </style>
38
+ """