vectara-agentic 0.2.5__py3-none-any.whl → 0.2.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of vectara-agentic might be problematic. Click here for more details.

vectara_agentic/agent.py CHANGED
@@ -8,7 +8,6 @@ from datetime import date
8
8
  import time
9
9
  import json
10
10
  import logging
11
- import traceback
12
11
  import asyncio
13
12
  import importlib
14
13
  from collections import Counter
@@ -17,24 +16,25 @@ import cloudpickle as pickle
17
16
 
18
17
  from dotenv import load_dotenv
19
18
 
20
- from retrying import retry
21
19
  from pydantic import Field, create_model, ValidationError
22
20
 
23
21
  from llama_index.core.memory import ChatMemoryBuffer
24
22
  from llama_index.core.llms import ChatMessage, MessageRole
25
23
  from llama_index.core.tools import FunctionTool
26
- from llama_index.core.agent import ReActAgent, StructuredPlannerAgent
24
+ from llama_index.core.agent import ReActAgent, StructuredPlannerAgent, FunctionCallingAgent
27
25
  from llama_index.core.agent.react.formatter import ReActChatFormatter
28
26
  from llama_index.agent.llm_compiler import LLMCompilerAgentWorker
29
27
  from llama_index.agent.lats import LATSAgentWorker
30
28
  from llama_index.core.callbacks import CallbackManager, TokenCountingHandler
31
29
  from llama_index.core.callbacks.base_handler import BaseCallbackHandler
32
30
  from llama_index.agent.openai import OpenAIAgent
31
+ from llama_index.core.agent.runner.base import AgentRunner
32
+ from llama_index.core.agent.types import BaseAgent
33
33
  from llama_index.core.workflow import Workflow
34
34
 
35
35
  from .types import (
36
36
  AgentType, AgentStatusType, LLMRole, ToolType,
37
- AgentResponse, AgentStreamingResponse,
37
+ AgentResponse, AgentStreamingResponse, AgentConfigType
38
38
  )
39
39
  from .utils import get_llm, get_tokenizer_for_model
40
40
  from ._prompts import (
@@ -103,9 +103,6 @@ def _get_llm_compiler_prompt(prompt: str, topic: str, custom_instructions: str)
103
103
  prompt += f"Today is {date.today().strftime('%A, %B %d, %Y')}"
104
104
  return prompt
105
105
 
106
- def _retry_if_exception(exception):
107
- # Define the condition to retry on certain exceptions
108
- return isinstance(exception, (TimeoutError))
109
106
 
110
107
  def get_field_type(field_schema: dict) -> Any:
111
108
  """
@@ -151,6 +148,7 @@ class Agent:
151
148
  agent_progress_callback: Optional[Callable[[AgentStatusType, str], None]] = None,
152
149
  query_logging_callback: Optional[Callable[[str, str], None]] = None,
153
150
  agent_config: Optional[AgentConfig] = None,
151
+ fallback_agent_config: Optional[AgentConfig] = None,
154
152
  chat_history: Optional[list[Tuple[str, str]]] = None,
155
153
  validate_tools: bool = False,
156
154
  workflow_cls: Workflow = None,
@@ -172,6 +170,8 @@ class Agent:
172
170
  query_logging_callback (Callable): A callback function the code calls upon completion of a query
173
171
  agent_config (AgentConfig, optional): The configuration of the agent.
174
172
  Defaults to AgentConfig(), which reads from environment variables.
173
+ fallback_agent_config (AgentConfig, optional): The fallback configuration of the agent.
174
+ This config is used when the main agent config fails multiple times.
175
175
  chat_history (Tuple[str, str], optional): A list of user/agent chat pairs to initialize the agent memory.
176
176
  validate_tools (bool, optional): Whether to validate tool inconsistency with instructions.
177
177
  Defaults to False.
@@ -179,12 +179,12 @@ class Agent:
179
179
  workflow_timeout (int, optional): The timeout for the workflow in seconds. Defaults to 120.
180
180
  """
181
181
  self.agent_config = agent_config or AgentConfig()
182
- self.agent_type = self.agent_config.agent_type
183
- self.use_structured_planning = use_structured_planning
182
+ self.agent_config_type = AgentConfigType.DEFAULT
184
183
  self.tools = tools
185
184
  if not any(tool.metadata.name == 'get_current_date' for tool in self.tools):
186
185
  self.tools += [ToolsFactory().create_tool(get_current_date)]
187
-
186
+ self.agent_type = self.agent_config.agent_type
187
+ self.use_structured_planning = use_structured_planning
188
188
  self.llm = get_llm(LLMRole.MAIN, config=self.agent_config)
189
189
  self._custom_instructions = custom_instructions
190
190
  self._topic = topic
@@ -231,7 +231,6 @@ class Agent:
231
231
  if self.tool_token_counter:
232
232
  callbacks.append(self.tool_token_counter)
233
233
  callback_manager = CallbackManager(callbacks) # type: ignore
234
- self.llm.callback_manager = callback_manager
235
234
  self.verbose = verbose
236
235
 
237
236
  if chat_history:
@@ -243,83 +242,130 @@ class Agent:
243
242
  else:
244
243
  self.memory = ChatMemoryBuffer.from_defaults(token_limit=128000)
245
244
 
246
- # Create agent based on type
247
- if self.agent_type == AgentType.REACT:
248
- prompt = _get_prompt(REACT_PROMPT_TEMPLATE, topic, custom_instructions)
249
- self.agent = ReActAgent.from_tools(
245
+ # Set up main agent and fallback agent
246
+ self.agent = self._create_agent(self.agent_config, callback_manager)
247
+ self.fallback_agent_config = fallback_agent_config
248
+ if self.fallback_agent_config:
249
+ self.fallback_agent = self._create_agent(self.fallback_agent_config, callback_manager)
250
+ else:
251
+ self.fallback_agent_config = None
252
+
253
+ # Setup observability
254
+ try:
255
+ self.observability_enabled = setup_observer(self.agent_config)
256
+ except Exception as e:
257
+ print(f"Failed to set up observer ({e}), ignoring")
258
+ self.observability_enabled = False
259
+
260
+ def _create_agent(
261
+ self,
262
+ config: AgentConfig,
263
+ llm_callback_manager: CallbackManager
264
+ ) -> Union[BaseAgent, AgentRunner]:
265
+ """
266
+ Creates the agent based on the configuration object.
267
+
268
+ Args:
269
+
270
+ config: The configuration of the agent.
271
+ llm_callback_manager: The callback manager for the agent's llm.
272
+
273
+ Returns:
274
+ Union[BaseAgent, AgentRunner]: The configured agent object.
275
+ """
276
+ agent_type = config.agent_type
277
+ llm = get_llm(LLMRole.MAIN, config=config)
278
+ llm.callback_manager = llm_callback_manager
279
+
280
+ if agent_type == AgentType.FUNCTION_CALLING:
281
+ prompt = _get_prompt(GENERAL_PROMPT_TEMPLATE, self._topic, self._custom_instructions)
282
+ agent = FunctionCallingAgent.from_tools(
283
+ tools=self.tools,
284
+ llm=llm,
285
+ memory=self.memory,
286
+ verbose=self.verbose,
287
+ max_function_calls=config.max_reasoning_steps,
288
+ callback_manager=llm_callback_manager,
289
+ system_prompt = prompt,
290
+ allow_parallel_tool_calls=True,
291
+ )
292
+ elif agent_type == AgentType.REACT:
293
+ prompt = _get_prompt(REACT_PROMPT_TEMPLATE, self._topic, self._custom_instructions)
294
+ agent = ReActAgent.from_tools(
250
295
  tools=self.tools,
251
- llm=self.llm,
296
+ llm=llm,
252
297
  memory=self.memory,
253
- verbose=verbose,
298
+ verbose=self.verbose,
254
299
  react_chat_formatter=ReActChatFormatter(system_header=prompt),
255
- max_iterations=self.agent_config.max_reasoning_steps,
256
- callable_manager=callback_manager,
300
+ max_iterations=config.max_reasoning_steps,
301
+ callable_manager=llm_callback_manager,
257
302
  )
258
- elif self.agent_type == AgentType.OPENAI:
259
- prompt = _get_prompt(GENERAL_PROMPT_TEMPLATE, topic, custom_instructions)
260
- self.agent = OpenAIAgent.from_tools(
303
+ elif agent_type == AgentType.OPENAI:
304
+ prompt = _get_prompt(GENERAL_PROMPT_TEMPLATE, self._topic, self._custom_instructions)
305
+ agent = OpenAIAgent.from_tools(
261
306
  tools=self.tools,
262
- llm=self.llm,
307
+ llm=llm,
263
308
  memory=self.memory,
264
- verbose=verbose,
265
- callable_manager=callback_manager,
266
- max_function_calls=self.agent_config.max_reasoning_steps,
309
+ verbose=self.verbose,
310
+ callback_manager=llm_callback_manager,
311
+ max_function_calls=config.max_reasoning_steps,
267
312
  system_prompt=prompt,
268
313
  )
269
- elif self.agent_type == AgentType.LLMCOMPILER:
314
+ elif agent_type == AgentType.LLMCOMPILER:
270
315
  agent_worker = LLMCompilerAgentWorker.from_tools(
271
316
  tools=self.tools,
272
- llm=self.llm,
273
- verbose=verbose,
274
- callable_manager=callback_manager,
317
+ llm=llm,
318
+ verbose=self.verbose,
319
+ callback_manager=llm_callback_manager,
275
320
  )
276
321
  agent_worker.system_prompt = _get_prompt(
277
- _get_llm_compiler_prompt(agent_worker.system_prompt, topic, custom_instructions),
278
- topic, custom_instructions
322
+ _get_llm_compiler_prompt(agent_worker.system_prompt, self._topic, self._custom_instructions),
323
+ self._topic, self._custom_instructions
279
324
  )
280
325
  agent_worker.system_prompt_replan = _get_prompt(
281
- _get_llm_compiler_prompt(agent_worker.system_prompt_replan, topic, custom_instructions),
282
- topic, custom_instructions
326
+ _get_llm_compiler_prompt(agent_worker.system_prompt_replan, self._topic, self._custom_instructions),
327
+ self._topic, self._custom_instructions
283
328
  )
284
- self.agent = agent_worker.as_agent()
285
- elif self.agent_type == AgentType.LATS:
329
+ agent = agent_worker.as_agent()
330
+ elif agent_type == AgentType.LATS:
286
331
  agent_worker = LATSAgentWorker.from_tools(
287
332
  tools=self.tools,
288
- llm=self.llm,
333
+ llm=llm,
289
334
  num_expansions=3,
290
335
  max_rollouts=-1,
291
- verbose=verbose,
292
- callable_manager=callback_manager,
336
+ verbose=self.verbose,
337
+ callback_manager=llm_callback_manager,
293
338
  )
294
- prompt = _get_prompt(REACT_PROMPT_TEMPLATE, topic, custom_instructions)
339
+ prompt = _get_prompt(REACT_PROMPT_TEMPLATE, self._topic, self._custom_instructions)
295
340
  agent_worker.chat_formatter = ReActChatFormatter(system_header=prompt)
296
- self.agent = agent_worker.as_agent()
341
+ agent = agent_worker.as_agent()
297
342
  else:
298
- raise ValueError(f"Unknown agent type: {self.agent_type}")
299
-
300
- try:
301
- self.observability_enabled = setup_observer(self.agent_config)
302
- except Exception as e:
303
- print(f"Failed to set up observer ({e}), ignoring")
304
- self.observability_enabled = False
343
+ raise ValueError(f"Unknown agent type: {agent_type}")
305
344
 
306
345
  # Set up structured planner if needed
307
346
  if (self.use_structured_planning
308
347
  or self.agent_type in [AgentType.LLMCOMPILER, AgentType.LATS]):
309
- self.agent = StructuredPlannerAgent(
310
- agent_worker=self.agent.agent_worker,
348
+ agent = StructuredPlannerAgent(
349
+ agent_worker=agent.agent_worker,
311
350
  tools=self.tools,
312
351
  memory=self.memory,
313
- verbose=verbose,
352
+ verbose=self.verbose,
314
353
  initial_plan_prompt=STRUCTURED_PLANNER_INITIAL_PLAN_PROMPT,
315
354
  plan_refine_prompt=STRUCTURED_PLANNER_PLAN_REFINE_PROMPT,
316
355
  )
317
356
 
357
+ return agent
358
+
318
359
  def clear_memory(self) -> None:
319
360
  """
320
361
  Clear the agent's memory.
321
362
  """
322
- self.agent.memory.reset()
363
+ if self.agent_config_type == AgentConfigType.DEFAULT:
364
+ self.agent.memory.reset()
365
+ elif self.agent_config_type == AgentConfigType.FALLBACK and self.fallback_agent_config:
366
+ self.fallback_agent.memory.reset()
367
+ else:
368
+ raise ValueError(f"Invalid agent config type {self.agent_config_type}")
323
369
 
324
370
  def __eq__(self, other):
325
371
  if not isinstance(other, Agent):
@@ -327,10 +373,10 @@ class Agent:
327
373
  return False
328
374
 
329
375
  # Compare agent_type
330
- if self.agent_type != other.agent_type:
376
+ if self.agent_config.agent_type != other.agent_config.agent_type:
331
377
  print(
332
- f"Comparison failed: agent_type differs. (self.agent_type: {self.agent_type}, "
333
- f"other.agent_type: {other.agent_type})"
378
+ f"Comparison failed: agent_type differs. (self.agent_config.agent_type: {self.agent_config.agent_type},"
379
+ f" other.agent_config.agent_type: {other.agent_config.agent_type})"
334
380
  )
335
381
  return False
336
382
 
@@ -360,7 +406,7 @@ class Agent:
360
406
  print(f"Comparison failed: verbose differs. (self.verbose: {self.verbose}, other.verbose: {other.verbose})")
361
407
  return False
362
408
 
363
- # Compare agent
409
+ # Compare agent memory
364
410
  if self.agent.memory.chat_store != other.agent.memory.chat_store:
365
411
  print(
366
412
  f"Comparison failed: agent memory differs. (self.agent: {repr(self.agent.memory.chat_store)}, "
@@ -383,7 +429,11 @@ class Agent:
383
429
  agent_progress_callback: Optional[Callable[[AgentStatusType, str], None]] = None,
384
430
  query_logging_callback: Optional[Callable[[str, str], None]] = None,
385
431
  agent_config: AgentConfig = AgentConfig(),
432
+ validate_tools: bool = False,
433
+ fallback_agent_config: Optional[AgentConfig] = None,
386
434
  chat_history: Optional[list[Tuple[str, str]]] = None,
435
+ workflow_cls: Workflow = None,
436
+ workflow_timeout: int = 120,
387
437
  ) -> "Agent":
388
438
  """
389
439
  Create an agent from tools, agent type, and language model.
@@ -398,7 +448,12 @@ class Agent:
398
448
  update_func (Callable): old name for agent_progress_callback. Will be deprecated in future.
399
449
  query_logging_callback (Callable): A callback function the code calls upon completion of a query
400
450
  agent_config (AgentConfig, optional): The configuration of the agent.
451
+ fallback_agent_config (AgentConfig, optional): The fallback configuration of the agent.
401
452
  chat_history (Tuple[str, str], optional): A list of user/agent chat pairs to initialize the agent memory.
453
+ validate_tools (bool, optional): Whether to validate tool inconsistency with instructions.
454
+ Defaults to False.
455
+ workflow_cls (Workflow, optional): The workflow class to be used with run(). Defaults to None.
456
+ workflow_timeout (int, optional): The timeout for the workflow in seconds. Defaults to 120.
402
457
 
403
458
  Returns:
404
459
  Agent: An instance of the Agent class.
@@ -409,6 +464,9 @@ class Agent:
409
464
  query_logging_callback=query_logging_callback,
410
465
  update_func=update_func, agent_config=agent_config,
411
466
  chat_history=chat_history,
467
+ validate_tools=validate_tools,
468
+ fallback_agent_config=fallback_agent_config,
469
+ workflow_cls = workflow_cls, workflow_timeout = workflow_timeout,
412
470
  )
413
471
 
414
472
  @classmethod
@@ -421,6 +479,9 @@ class Agent:
421
479
  vectara_api_key: str = str(os.environ.get("VECTARA_API_KEY", "")),
422
480
  agent_progress_callback: Optional[Callable[[AgentStatusType, str], None]] = None,
423
481
  query_logging_callback: Optional[Callable[[str, str], None]] = None,
482
+ agent_config: AgentConfig = AgentConfig(),
483
+ fallback_agent_config: Optional[AgentConfig] = None,
484
+ chat_history: Optional[list[Tuple[str, str]]] = None,
424
485
  verbose: bool = False,
425
486
  vectara_filter_fields: list[dict] = [],
426
487
  vectara_offset: int = 0,
@@ -456,6 +517,9 @@ class Agent:
456
517
  vectara_api_key (str): The Vectara API key.
457
518
  agent_progress_callback (Callable): A callback function the code calls on any agent updates.
458
519
  query_logging_callback (Callable): A callback function the code calls upon completion of a query
520
+ agent_config (AgentConfig, optional): The configuration of the agent.
521
+ fallback_agent_config (AgentConfig, optional): The fallback configuration of the agent.
522
+ chat_history (Tuple[str, str], optional): A list of user/agent chat pairs to initialize the agent memory.
459
523
  data_description (str): The description of the data.
460
524
  assistant_specialty (str): The specialty of the assistant.
461
525
  verbose (bool, optional): Whether to print verbose output.
@@ -557,22 +621,41 @@ class Agent:
557
621
  verbose=verbose,
558
622
  agent_progress_callback=agent_progress_callback,
559
623
  query_logging_callback=query_logging_callback,
624
+ agent_config=agent_config,
625
+ fallback_agent_config=fallback_agent_config,
626
+ chat_history=chat_history,
560
627
  )
561
628
 
562
- def report(self) -> None:
629
+ def _switch_agent_config(self) -> None:
630
+ """"
631
+ Switch the configuration type of the agent.
632
+ This function is called automatically to switch the agent configuration if the current configuration fails.
633
+ """
634
+ if self.agent_config_type == AgentConfigType.DEFAULT:
635
+ self.agent_config_type = AgentConfigType.FALLBACK
636
+ else:
637
+ self.agent_config_type = AgentConfigType.DEFAULT
638
+
639
+ def report(self, detailed: bool = False) -> None:
563
640
  """
564
641
  Get a report from the agent.
565
642
 
643
+ Args:
644
+ detailed (bool, optional): Whether to include detailed information. Defaults to False.
645
+
566
646
  Returns:
567
647
  str: The report from the agent.
568
648
  """
569
649
  print("Vectara agentic Report:")
570
- print(f"Agent Type = {self.agent_type}")
650
+ print(f"Agent Type = {self.agent_config.agent_type}")
571
651
  print(f"Topic = {self._topic}")
572
652
  print("Tools:")
573
653
  for tool in self.tools:
574
654
  if hasattr(tool, 'metadata'):
575
- print(f"- {tool.metadata.name}")
655
+ if detailed:
656
+ print(f"- {tool.metadata.name} - {tool.metadata.description}")
657
+ else:
658
+ print(f"- {tool.metadata.name}")
576
659
  else:
577
660
  print("- tool without metadata")
578
661
  print(f"Agent LLM = {get_llm(LLMRole.MAIN, config=self.agent_config).metadata.model_name}")
@@ -590,13 +673,27 @@ class Agent:
590
673
  "tool token count": self.tool_token_counter.total_llm_token_count if self.tool_token_counter else -1,
591
674
  }
592
675
 
676
+ def _get_current_agent(self):
677
+ return self.agent if self.agent_config_type == AgentConfigType.DEFAULT else self.fallback_agent
678
+
679
+ def _get_current_agent_type(self):
680
+ return (
681
+ self.agent_config.agent_type if self.agent_config_type == AgentConfigType.DEFAULT
682
+ else self.fallback_agent_config.agent_type
683
+ )
684
+
593
685
  async def _aformat_for_lats(self, prompt, agent_response):
594
686
  llm_prompt = f"""
595
687
  Given the question '{prompt}', and agent response '{agent_response.response}',
596
688
  Please provide a well formatted final response to the query.
597
689
  final response:
598
690
  """
599
- agent_response.response = str(await self.llm.acomplete(llm_prompt))
691
+ agent_type = self._get_current_agent_type()
692
+ if agent_type != AgentType.LATS:
693
+ return
694
+
695
+ agent = self._get_current_agent()
696
+ agent_response.response = str(agent.llm.acomplete(llm_prompt))
600
697
 
601
698
  def chat(self, prompt: str) -> AgentResponse: # type: ignore
602
699
  """
@@ -610,12 +707,7 @@ class Agent:
610
707
  """
611
708
  return asyncio.run(self.achat(prompt))
612
709
 
613
- @retry(
614
- retry_on_exception=_retry_if_exception,
615
- stop_max_attempt_number=3,
616
- wait_fixed=2000,
617
- )
618
- async def achat(self, prompt: str) -> AgentResponse: # type: ignore
710
+ async def achat(self, prompt: str) -> AgentResponse: # type: ignore
619
711
  """
620
712
  Interact with the agent using a chat prompt.
621
713
 
@@ -625,25 +717,36 @@ class Agent:
625
717
  Returns:
626
718
  AgentResponse: The response from the agent.
627
719
  """
628
- try:
629
- st = time.time()
630
- agent_response = await self.agent.achat(prompt)
631
- if self.agent_type == AgentType.LATS:
720
+ max_attempts = 4 if self.fallback_agent_config else 2
721
+ attempt = 0
722
+ orig_llm = self.llm.metadata.model_name
723
+ last_error = None
724
+ while attempt < max_attempts:
725
+ try:
726
+ current_agent = self._get_current_agent()
727
+ agent_response = await current_agent.achat(prompt)
632
728
  await self._aformat_for_lats(prompt, agent_response)
633
- if self.verbose:
634
- print(f"Time taken: {time.time() - st}")
635
- if self.observability_enabled:
636
- eval_fcs()
637
- if self.query_logging_callback:
638
- self.query_logging_callback(prompt, agent_response.response)
639
- return agent_response
640
- except Exception as e:
641
- return AgentResponse(
642
- response = (
643
- f"Vectara Agentic: encountered an exception ({e}) at ({traceback.format_exc()})"
644
- ", and can't respond."
645
- )
729
+ if self.observability_enabled:
730
+ eval_fcs()
731
+ if self.query_logging_callback:
732
+ self.query_logging_callback(prompt, agent_response.response)
733
+ return agent_response
734
+
735
+ except Exception as e:
736
+ last_error = e
737
+ if attempt >= 2:
738
+ if self.verbose:
739
+ print(f"LLM call failed on attempt {attempt}. Switching agent configuration.")
740
+ self._switch_agent_config()
741
+ time.sleep(1)
742
+ attempt += 1
743
+
744
+ return AgentResponse(
745
+ response=(
746
+ f"For {orig_llm} LLM - failure can't be resolved after "
747
+ f"{max_attempts} attempts ({last_error}."
646
748
  )
749
+ )
647
750
 
648
751
  def stream_chat(self, prompt: str) -> AgentStreamingResponse: # type: ignore
649
752
  """
@@ -655,11 +758,6 @@ class Agent:
655
758
  """
656
759
  return asyncio.run(self.astream_chat(prompt))
657
760
 
658
- @retry(
659
- retry_on_exception=_retry_if_exception,
660
- stop_max_attempt_number=3,
661
- wait_fixed=2000,
662
- )
663
761
  async def astream_chat(self, prompt: str) -> AgentStreamingResponse: # type: ignore
664
762
  """
665
763
  Interact with the agent using a chat prompt asynchronously with streaming.
@@ -668,29 +766,39 @@ class Agent:
668
766
  Returns:
669
767
  AgentStreamingResponse: The streaming response from the agent.
670
768
  """
671
- try:
672
- agent_response = await self.agent.astream_chat(prompt)
673
- original_async_response_gen = agent_response.async_response_gen
674
-
675
- # Wrap async_response_gen
676
- async def _stream_response_wrapper():
677
- async for token in original_async_response_gen():
678
- yield token # Yield async token to keep streaming behavior
679
-
680
- # After streaming completes, execute additional logic
681
- if self.agent_type == AgentType.LATS:
769
+ max_attempts = 4 if self.fallback_agent_config else 2
770
+ attempt = 0
771
+ while attempt < max_attempts:
772
+ try:
773
+ current_agent = self._get_current_agent()
774
+ agent_response = await current_agent.astream_chat(prompt)
775
+ original_async_response_gen = agent_response.async_response_gen
776
+
777
+ # Define a wrapper to preserve streaming behavior while executing post-stream logic.
778
+ async def _stream_response_wrapper():
779
+ async for token in original_async_response_gen():
780
+ yield token # Yield tokens as they are generated
781
+ # Post-streaming additional logic:
682
782
  await self._aformat_for_lats(prompt, agent_response)
683
- if self.query_logging_callback:
684
- self.query_logging_callback(prompt, agent_response.response)
685
- if self.observability_enabled:
686
- eval_fcs()
687
-
688
- agent_response.async_response_gen = _stream_response_wrapper # Override method
689
- return agent_response
690
- except Exception as e:
691
- raise ValueError(
692
- f"Vectara Agentic: encountered an exception ({e}) at ({traceback.format_exc()}), and can't respond."
693
- ) from e
783
+ if self.query_logging_callback:
784
+ self.query_logging_callback(prompt, agent_response.response)
785
+ if self.observability_enabled:
786
+ eval_fcs()
787
+
788
+ agent_response.async_response_gen = _stream_response_wrapper # Override the generator
789
+ return agent_response
790
+
791
+ except Exception:
792
+ if attempt >= 2:
793
+ if self.verbose:
794
+ print("LLM call failed. Switching agent configuration.")
795
+ self._switch_agent_config()
796
+ time.sleep(1)
797
+ attempt += 1
798
+
799
+ return AgentResponse(
800
+ response=f"LLM failure can't be resolved after {max_attempts} attempts."
801
+ )
694
802
 
695
803
  #
696
804
  # run() method for running a workflow
@@ -783,13 +891,14 @@ class Agent:
783
891
  tool_info.append(tool_dict)
784
892
 
785
893
  return {
786
- "agent_type": self.agent_type.value,
894
+ "agent_type": self.agent_config.agent_type.value,
787
895
  "memory": pickle.dumps(self.agent.memory).decode("latin-1"),
788
896
  "tools": tool_info,
789
897
  "topic": self._topic,
790
898
  "custom_instructions": self._custom_instructions,
791
899
  "verbose": self.verbose,
792
900
  "agent_config": self.agent_config.to_dict(),
901
+ "fallback_agent": self.fallback_agent_config.to_dict() if self.fallback_agent_config else None,
793
902
  "workflow_cls": self.workflow_cls if self.workflow_cls else None,
794
903
  }
795
904
 
@@ -797,6 +906,11 @@ class Agent:
797
906
  def from_dict(cls, data: Dict[str, Any]) -> "Agent":
798
907
  """Create an Agent instance from a dictionary."""
799
908
  agent_config = AgentConfig.from_dict(data["agent_config"])
909
+ fallback_agent_config = (
910
+ AgentConfig.from_dict(data["fallback_agent_config"])
911
+ if data.get("fallback_agent_config")
912
+ else None
913
+ )
800
914
  tools = []
801
915
 
802
916
  for tool_data in data["tools"]:
@@ -850,6 +964,7 @@ class Agent:
850
964
  topic=data["topic"],
851
965
  custom_instructions=data["custom_instructions"],
852
966
  verbose=data["verbose"],
967
+ fallback_agent_config=fallback_agent_config,
853
968
  workflow_cls=data["workflow_cls"],
854
969
  )
855
970
  memory = pickle.loads(data["memory"].encode("latin-1")) if data.get("memory") else None