ofermend commited on
Commit
f2514fd
·
1 Parent(s): 0f782f3
Files changed (4) hide show
  1. Dockerfile +4 -1
  2. agent.py +34 -28
  3. requirements.txt +2 -2
  4. st_app.py +2 -11
Dockerfile CHANGED
@@ -7,12 +7,15 @@ COPY ./requirements.txt /app/requirements.txt
7
  RUN pip3 install --no-cache-dir --upgrade pip
8
  RUN pip3 install --no-cache-dir wheel setuptools build
9
  RUN pip3 install --no-cache-dir --use-pep517 -r /app/requirements.txt
10
-
11
  # User
12
  RUN useradd -m -u 1000 user
13
  USER user
14
  ENV HOME /home/user
15
  ENV PATH $HOME/.local/bin:$PATH
 
 
 
16
 
17
  WORKDIR $HOME
18
  RUN mkdir app
 
7
  RUN pip3 install --no-cache-dir --upgrade pip
8
  RUN pip3 install --no-cache-dir wheel setuptools build
9
  RUN pip3 install --no-cache-dir --use-pep517 -r /app/requirements.txt
10
+
11
  # User
12
  RUN useradd -m -u 1000 user
13
  USER user
14
  ENV HOME /home/user
15
  ENV PATH $HOME/.local/bin:$PATH
16
+ ENV TIKTOKEN_CACHE_DIR $HOME/.cache/tiktoken
17
+
18
+ RUN mkdir -p $HOME/.cache/tiktoken
19
 
20
  WORKDIR $HOME
21
  RUN mkdir app
agent.py CHANGED
@@ -10,7 +10,7 @@ from omegaconf import OmegaConf
10
  from vectara_agentic.agent import Agent
11
  from vectara_agentic.tools import ToolsFactory, VectaraToolFactory
12
  from vectara_agentic.agent_config import AgentConfig
13
- from vectara_agentic.sub_query_workflow import SequentialSubQuestionsWorkflow
14
 
15
  from dotenv import load_dotenv
16
  load_dotenv(override=True)
@@ -33,7 +33,7 @@ tickers = {
33
  "STT": "State Street",
34
  "BK": "Bank of New York Mellon",
35
  }
36
- years = range(2015, 2025)
37
  initial_prompt = "How can I help you today?"
38
 
39
 
@@ -102,47 +102,36 @@ class AgentTools:
102
 
103
  def get_tools(self):
104
  class QueryTranscriptsArgs(BaseModel):
105
- query: str = Field(..., description="The user query, always in the form of a question", examples=["what are the risks reported?", "who are the competitors?"])
106
  year: int | str = Field(
107
  default=None,
108
  description=f"The year this query relates to. An integer between {min(years)} and {max(years)} or a string specifying a condition on the year",
109
  examples=[2020, '>2021', '<2023', '>=2021', '<=2023', '[2021, 2023]', '[2021, 2023)']
110
  )
111
- ticker: str = Field(..., description=f"The company ticker this query relates to. Must be a valid ticket symbol from the list {list(tickers.keys())}.")
112
 
113
- vec_factory = VectaraToolFactory(vectara_api_key=self.cfg.api_key,
114
- vectara_corpus_key=self.cfg.corpus_key)
115
  summarizer = 'vectara-summary-table-md-query-ext-jan-2025-gpt-4o'
116
- ask_transcripts = vec_factory.create_rag_tool(
117
  tool_name = "ask_transcripts",
118
  tool_description = """
119
- Given a company name and year, responds to a user question about the company, based on analyst call transcripts about the company's financial reports for that year.
120
- You can ask this tool any question about the company including risks, opportunities, financial performance, competitors and more.
121
  """,
122
  tool_args_schema = QueryTranscriptsArgs,
123
- reranker = "multilingual_reranker_v1", rerank_k = 100, rerank_cutoff = 0.2,
124
- n_sentences_before = 2, n_sentences_after = 4, lambda_val = 0.005,
125
  summary_num_results = 15,
126
  vectara_summarizer = summarizer,
 
127
  include_citations = True,
 
128
  verbose = False,
129
  )
130
 
131
- class SearchTranscriptsArgs(BaseModel):
132
- query: str = Field(..., description="The user query, always in the form of a question", examples=["what are the risks reported?", "who are the competitors?"])
133
- top_k: int = Field(..., description="The number of results to return.")
134
- year: int | str = Field(
135
- default=None,
136
- description=f"The year this query relates to. An integer between {min(years)} and {max(years)} or a string specifying a condition on the year",
137
- examples=[2020, '>2021', '<2023', '>=2021', '<=2023', '[2021, 2023]', '[2021, 2023)']
138
- )
139
- ticker: str = Field(..., description=f"The company ticker this query relates to. Must be a valid ticket symbol from the list {list(tickers.keys())}.")
140
- search_transcripts = vec_factory.create_search_tool(
141
  tool_name = "search_transcripts",
142
  tool_description = """
143
- Given a company name and year, and a user query, retrieves relevant documents about the company.
144
  """,
145
- tool_args_schema = SearchTranscriptsArgs,
146
  reranker = "multilingual_reranker_v1", rerank_k = 100,
147
  lambda_val = 0.005,
148
  verbose=False
@@ -156,13 +145,14 @@ class AgentTools:
156
  get_valid_years,
157
  fmp_income_statement,
158
  ]
159
- ] +
160
  [ask_transcripts, search_transcripts]
161
  )
162
 
163
  def initialize_agent(_cfg, agent_progress_callback=None) -> Agent:
164
  financial_bot_instructions = """
165
- - You are a helpful financial assistant, with expertise in financial reporting, in conversation with a user.
 
166
  - Use the 'fmp_income_statement' tool (with the company ticker and year) to obtain financial data.
167
  - Always check the 'get_company_info' and 'get_valid_years' tools to validate company and year are valid.
168
  - Use the 'ask_transcripts' tool to answer most questions about the company's financial performance, risks, opportunities, strategy, competitors, and more.
@@ -177,19 +167,35 @@ def initialize_agent(_cfg, agent_progress_callback=None) -> Agent:
177
  def query_logging(query: str, response: str):
178
  print(f"Logging query={query}, response={response}")
179
 
180
- agent_config = AgentConfig()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
 
182
  agent = Agent(
 
 
183
  tools=AgentTools(_cfg, agent_config).get_tools(),
184
  topic="Financial data, annual reports and 10-K filings",
185
  custom_instructions=financial_bot_instructions,
186
  agent_progress_callback=agent_progress_callback,
187
  query_logging_callback=query_logging,
188
  verbose=True,
189
- workflow_cls=SequentialSubQuestionsWorkflow,
190
  )
191
 
192
- agent.report()
193
  return agent
194
 
195
  def get_agent_config() -> OmegaConf:
 
10
  from vectara_agentic.agent import Agent
11
  from vectara_agentic.tools import ToolsFactory, VectaraToolFactory
12
  from vectara_agentic.agent_config import AgentConfig
13
+ from vectara_agentic.types import ModelProvider, AgentType
14
 
15
  from dotenv import load_dotenv
16
  load_dotenv(override=True)
 
33
  "STT": "State Street",
34
  "BK": "Bank of New York Mellon",
35
  }
36
+ years = list(range(2015, 2025))
37
  initial_prompt = "How can I help you today?"
38
 
39
 
 
102
 
103
  def get_tools(self):
104
  class QueryTranscriptsArgs(BaseModel):
105
+ ticker: str = Field(description="The ticker symbol for the company", examples=list(tickers.keys()), default=None)
106
  year: int | str = Field(
107
  default=None,
108
  description=f"The year this query relates to. An integer between {min(years)} and {max(years)} or a string specifying a condition on the year",
109
  examples=[2020, '>2021', '<2023', '>=2021', '<=2023', '[2021, 2023]', '[2021, 2023)']
110
  )
 
111
 
 
 
112
  summarizer = 'vectara-summary-table-md-query-ext-jan-2025-gpt-4o'
113
+ ask_transcripts = self.vec_factory.create_rag_tool(
114
  tool_name = "ask_transcripts",
115
  tool_description = """
116
+ Answer questions about a company (using its ticker) including risks, opportunities, financial performance, competitors and more, based on earnings calls transcripts.
 
117
  """,
118
  tool_args_schema = QueryTranscriptsArgs,
119
+ reranker = "multilingual_reranker_v1", rerank_k = 100, rerank_cutoff = 0.3,
120
+ n_sentences_before = 2, n_sentences_after = 2, lambda_val = 0.005,
121
  summary_num_results = 15,
122
  vectara_summarizer = summarizer,
123
+ max_tokens = 4096, max_response_chars = 8192,
124
  include_citations = True,
125
+ save_history = True,
126
  verbose = False,
127
  )
128
 
129
+ search_transcripts = self.vec_factory.create_search_tool(
 
 
 
 
 
 
 
 
 
130
  tool_name = "search_transcripts",
131
  tool_description = """
132
+ retrieves relevant earning call transcripts about a company (using its ticker).
133
  """,
134
+ tool_args_schema = QueryTranscriptsArgs,
135
  reranker = "multilingual_reranker_v1", rerank_k = 100,
136
  lambda_val = 0.005,
137
  verbose=False
 
145
  get_valid_years,
146
  fmp_income_statement,
147
  ]
148
+ ] +
149
  [ask_transcripts, search_transcripts]
150
  )
151
 
152
  def initialize_agent(_cfg, agent_progress_callback=None) -> Agent:
153
  financial_bot_instructions = """
154
+ - You are a helpful financial assistant, with expertise in financial reporting, in conversation with a user.
155
+ - Never base your on general industry knowledge, only use information from tool calls.
156
  - Use the 'fmp_income_statement' tool (with the company ticker and year) to obtain financial data.
157
  - Always check the 'get_company_info' and 'get_valid_years' tools to validate company and year are valid.
158
  - Use the 'ask_transcripts' tool to answer most questions about the company's financial performance, risks, opportunities, strategy, competitors, and more.
 
167
  def query_logging(query: str, response: str):
168
  print(f"Logging query={query}, response={response}")
169
 
170
+ agent_config = AgentConfig(
171
+ agent_type = os.getenv("VECTARA_AGENTIC_AGENT_TYPE", AgentType.OPENAI.value),
172
+ main_llm_provider = os.getenv("VECTARA_AGENTIC_MAIN_LLM_PROVIDER", ModelProvider.OPENAI.value),
173
+ main_llm_model_name = os.getenv("VECTARA_AGENTIC_MAIN_MODEL_NAME", ""),
174
+ tool_llm_provider = os.getenv("VECTARA_AGENTIC_TOOL_LLM_PROVIDER", ModelProvider.OPENAI.value),
175
+ tool_llm_model_name = os.getenv("VECTARA_AGENTIC_TOOL_MODEL_NAME", ""),
176
+ observer = os.getenv("VECTARA_AGENTIC_OBSERVER_TYPE", "NO_OBSERVER")
177
+ )
178
+ fallback_agent_config = AgentConfig(
179
+ agent_type = os.getenv("VECTARA_AGENTIC_FALLBACK_AGENT_TYPE", AgentType.OPENAI.value),
180
+ main_llm_provider = os.getenv("VECTARA_AGENTIC_FALLBACK_MAIN_LLM_PROVIDER", ModelProvider.OPENAI.value),
181
+ main_llm_model_name = os.getenv("VECTARA_AGENTIC_FALLBACK_MAIN_MODEL_NAME", ""),
182
+ tool_llm_provider = os.getenv("VECTARA_AGENTIC_FALLBACK_TOOL_LLM_PROVIDER", ModelProvider.OPENAI.value),
183
+ tool_llm_model_name = os.getenv("VECTARA_AGENTIC_FALLBACK_TOOL_MODEL_NAME", ""),
184
+ observer = os.getenv("VECTARA_AGENTIC_OBSERVER_TYPE", "NO_OBSERVER")
185
+ )
186
 
187
  agent = Agent(
188
+ agent_config=agent_config,
189
+ fallback_agent_config=fallback_agent_config,
190
  tools=AgentTools(_cfg, agent_config).get_tools(),
191
  topic="Financial data, annual reports and 10-K filings",
192
  custom_instructions=financial_bot_instructions,
193
  agent_progress_callback=agent_progress_callback,
194
  query_logging_callback=query_logging,
195
  verbose=True,
 
196
  )
197
 
198
+ agent.report(detailed=False)
199
  return agent
200
 
201
  def get_agent_config() -> OmegaConf:
requirements.txt CHANGED
@@ -1,9 +1,9 @@
1
  omegaconf==2.3.0
2
  python-dotenv==1.0.1
3
- streamlit==1.43.2
4
  streamlit_feedback==0.1.3
5
  uuid==1.30
6
  langdetect==1.0.9
7
  langcodes==3.4.0
8
- vectara-agentic==0.2.11
9
  torch==2.6.0
 
1
  omegaconf==2.3.0
2
  python-dotenv==1.0.1
3
+ streamlit==1.45.0
4
  streamlit_feedback==0.1.3
5
  uuid==1.30
6
  langdetect==1.0.9
7
  langcodes==3.4.0
8
+ vectara-agentic==0.2.15
9
  torch==2.6.0
st_app.py CHANGED
@@ -14,22 +14,12 @@ from agent import initialize_agent, get_agent_config
14
 
15
  initial_prompt = "How can I help you today?"
16
 
17
- # def pil_to_base64(img):
18
- # buffered = BytesIO()
19
- # img.save(buffered, format="PNG")
20
- # return base64.b64encode(buffered.getvalue()).decode()
21
-
22
-
23
  def format_log_msg(log_msg: str):
24
  max_log_msg_size = 500
25
  return log_msg if len(log_msg) <= max_log_msg_size else log_msg[:max_log_msg_size]+'...'
26
 
27
  def agent_progress_callback(status_type: AgentStatusType, msg: str):
28
  output = f'<span style="color:blue;">{status_type.value}</span>: {msg}'
29
- if "log_messages" not in st.session_state:
30
- st.session_state.log_messages = [output]
31
- else:
32
- st.session_state.log_messages.append(output)
33
  st.session_state.log_messages.append(output)
34
  if 'status' in st.session_state:
35
  latest_message = ''
@@ -85,6 +75,7 @@ async def launch_bot():
85
  else:
86
  st.session_state.agent.clear_memory()
87
 
 
88
  if 'cfg' not in st.session_state:
89
  cfg = get_agent_config()
90
  st.session_state.cfg = cfg
@@ -154,7 +145,7 @@ async def launch_bot():
154
  if st.session_state.prompt:
155
  with st.chat_message("assistant", avatar='🤖'):
156
  st.session_state.status = st.status('Processing...', expanded=False)
157
- response = st.session_state.agent.chat(st.session_state.prompt)
158
  res = escape_dollars_outside_latex(response.response)
159
 
160
  #from vectara_agentic.sub_query_workflow import SequentialSubQuestionsWorkflow
 
14
 
15
  initial_prompt = "How can I help you today?"
16
 
 
 
 
 
 
 
17
  def format_log_msg(log_msg: str):
18
  max_log_msg_size = 500
19
  return log_msg if len(log_msg) <= max_log_msg_size else log_msg[:max_log_msg_size]+'...'
20
 
21
  def agent_progress_callback(status_type: AgentStatusType, msg: str):
22
  output = f'<span style="color:blue;">{status_type.value}</span>: {msg}'
 
 
 
 
23
  st.session_state.log_messages.append(output)
24
  if 'status' in st.session_state:
25
  latest_message = ''
 
75
  else:
76
  st.session_state.agent.clear_memory()
77
 
78
+
79
  if 'cfg' not in st.session_state:
80
  cfg = get_agent_config()
81
  st.session_state.cfg = cfg
 
145
  if st.session_state.prompt:
146
  with st.chat_message("assistant", avatar='🤖'):
147
  st.session_state.status = st.status('Processing...', expanded=False)
148
+ response = await st.session_state.agent.achat(st.session_state.prompt)
149
  res = escape_dollars_outside_latex(response.response)
150
 
151
  #from vectara_agentic.sub_query_workflow import SequentialSubQuestionsWorkflow