lollms-client 0.29.2__py3-none-any.whl → 0.31.0__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 lollms-client might be problematic. Click here for more details.

lollms_client/__init__.py CHANGED
@@ -8,7 +8,7 @@ from lollms_client.lollms_utilities import PromptReshaper # Keep general utiliti
8
8
  from lollms_client.lollms_mcp_binding import LollmsMCPBinding, LollmsMCPBindingManager
9
9
  from lollms_client.lollms_llm_binding import LollmsLLMBindingManager
10
10
 
11
- __version__ = "0.29.2" # Updated version
11
+ __version__ = "0.31.0" # Updated version
12
12
 
13
13
  # Optionally, you could define __all__ if you want to be explicit about exports
14
14
  __all__ = [
@@ -11,6 +11,7 @@ from typing import Optional, Callable, List, Union, Dict
11
11
 
12
12
  from ascii_colors import ASCIIColors, trace_exception
13
13
  import pipmaster as pm
14
+ from lollms_client.lollms_utilities import ImageTokenizer
14
15
  pm.ensure_packages(["ollama","pillow","tiktoken"])
15
16
 
16
17
 
@@ -468,6 +469,24 @@ class OllamaBinding(LollmsLLMBinding):
468
469
  return -1
469
470
  #return count_tokens_ollama(text, self.model_name, self.ollama_client)
470
471
  return len(self.tokenize(text))
472
+
473
+ def count_image_tokens(self, image: str) -> int:
474
+ """
475
+ Estimate the number of tokens for an image using ImageTokenizer based on self.model_name.
476
+
477
+ Args:
478
+ image (str): Image to count tokens from. Either base64 string, path to image file, or URL.
479
+
480
+ Returns:
481
+ int: Estimated number of tokens for the image. Returns -1 on error.
482
+ """
483
+ try:
484
+ # Delegate token counting to ImageTokenizer
485
+ return ImageTokenizer(self.model_name).count_image_tokens(image)
486
+ except Exception as e:
487
+ ASCIIColors.warning(f"Could not estimate image tokens: {e}")
488
+ return -1
489
+
471
490
  def embed(self, text: str, **kwargs) -> List[float]:
472
491
  """
473
492
  Get embeddings for the input text using Ollama API.
@@ -147,9 +147,6 @@ class LollmsClient():
147
147
  available = self.binding_manager.get_available_bindings()
148
148
  raise ValueError(f"Failed to create LLM binding: {binding_name}. Available: {available}")
149
149
 
150
- # Determine the effective host address (use LLM binding's if initial was None)
151
- effective_host_address = self.host_address
152
-
153
150
  # --- Modality Binding Setup ---
154
151
  self.tts_binding_manager = LollmsTTSBindingManager(tts_bindings_dir)
155
152
  self.tti_binding_manager = LollmsTTIBindingManager(tti_bindings_dir)
@@ -433,7 +430,21 @@ class LollmsClient():
433
430
  if self.binding:
434
431
  return self.binding.count_tokens(text)
435
432
  raise RuntimeError("LLM binding not initialized.")
436
-
433
+
434
+ def count_image_tokens(self, image: str) -> int:
435
+ """
436
+ Estimate the number of tokens for an image using ImageTokenizer based on self.model_name.
437
+
438
+ Args:
439
+ image (str): Image to count tokens from. Either base64 string, path to image file, or URL.
440
+
441
+ Returns:
442
+ int: Estimated number of tokens for the image. Returns -1 on error.
443
+ """
444
+ if self.binding:
445
+ return self.binding.count_image_tokens(image)
446
+ raise RuntimeError("LLM binding not initialized.")
447
+
437
448
  def get_model_details(self) -> dict:
438
449
  """
439
450
  Get model information from the active LLM binding.
@@ -1577,25 +1588,25 @@ Provide your response as a single JSON object inside a JSON markdown tag. Use th
1577
1588
 
1578
1589
  # Add the new put_code_in_buffer tool definition
1579
1590
  available_tools.append({
1580
- "name": "put_code_in_buffer",
1591
+ "name": "local_tools::put_code_in_buffer",
1581
1592
  "description": """Generates and stores code into a buffer to be used by another tool. You can put the uuid of the generated code into the fields that require long code among the tools. If no tool requires code as input do not use put_code_in_buffer. put_code_in_buffer do not execute the code nor does it audit it.""",
1582
1593
  "input_schema": {"type": "object", "properties": {"prompt": {"type": "string", "description": "A detailed natural language description of the code's purpose and requirements."}, "language": {"type": "string", "description": "The programming language of the generated code. By default it uses python."}}, "required": ["prompt"]}
1583
1594
  })
1584
1595
  available_tools.append({
1585
- "name": "view_generated_code",
1596
+ "name": "local_tools::view_generated_code",
1586
1597
  "description": """Views the code that was generated and stored to the buffer. You need to have a valid uuid of the generated code.""",
1587
1598
  "input_schema": {"type": "object", "properties": {"code_id": {"type": "string", "description": "The case sensitive uuid of the generated code."}}, "required": ["uuid"]}
1588
1599
  })
1589
1600
  # Add the new refactor_scratchpad tool definition
1590
1601
  available_tools.append({
1591
- "name": "refactor_scratchpad",
1602
+ "name": "local_tools::refactor_scratchpad",
1592
1603
  "description": "Rewrites the scratchpad content to clean it and reorganize it. Only use if the scratchpad is messy or contains too much information compared to what you need.",
1593
1604
  "input_schema": {"type": "object", "properties": {}}
1594
1605
  })
1595
1606
 
1596
1607
  formatted_tools_list = "\n".join([f"**{t['name']}**:\n{t['description']}\ninput schema:\n{json.dumps(t['input_schema'])}" for t in available_tools])
1597
- formatted_tools_list += "\n**request_clarification**:\nUse if the user's request is ambiguous and you can not infer a clear idea of his intent. this tool has no parameters."
1598
- formatted_tools_list += "\n**final_answer**:\nUse when you are ready to respond to the user. this tool has no parameters."
1608
+ formatted_tools_list += "\n**local_tools::request_clarification**:\nUse if the user's request is ambiguous and you can not infer a clear idea of his intent. this tool has no parameters."
1609
+ formatted_tools_list += "\n**local_tools::final_answer**:\nUse when you are ready to respond to the user. this tool has no parameters."
1599
1610
 
1600
1611
  if discovery_step_id: log_event(f"**Discovering tools** found {len(available_tools)} tools",MSG_TYPE.MSG_TYPE_STEP_END, event_id=discovery_step_id)
1601
1612
 
@@ -1621,15 +1632,16 @@ Provide your response as a single JSON object inside a JSON markdown tag. Use th
1621
1632
  - Does the latest observation completely fulfill the user's original request?
1622
1633
  - If YES, your next action MUST be to use the `final_answer` tool.
1623
1634
  - If NO, what is the single next logical step needed? This may involve writing code first with `put_code_in_buffer`, then using another tool.
1624
- - If you are stuck or the request is ambiguous, use `request_clarification`.
1635
+ - If you are stuck or the request is ambiguous, use `local_tools::request_clarification`.
1625
1636
  3. **ACT:** Formulate your decision as a JSON object.
1637
+ ** Important ** Always use this format alias::tool_name to call the tool
1626
1638
  """
1627
1639
  action_template = {
1628
1640
  "thought": "My detailed analysis of the last observation and my reasoning for the next action and how it integrates with my global plan.",
1629
1641
  "action": {
1630
- "tool_name": "The single tool to use (e.g., 'put_code_in_buffer', 'time_machine::get_current_time', 'final_answer').",
1642
+ "tool_name": "The single tool to use (e.g., 'local_tools::put_code_in_buffer', 'local_tools::final_answer').",
1631
1643
  "tool_params": {"param1": "value1"},
1632
- "clarification_question": "(string, ONLY if tool_name is 'request_clarification')"
1644
+ "clarification_question": "(string, ONLY if tool_name is 'local_tools::request_clarification')"
1633
1645
  }
1634
1646
  }
1635
1647
  if debug: log_prompt(reasoning_prompt_template, f"REASONING PROMPT (Step {i+1})")
@@ -1667,18 +1679,22 @@ Provide your response as a single JSON object inside a JSON markdown tag. Use th
1667
1679
  break
1668
1680
 
1669
1681
  # --- Handle special, non-executing tools ---
1670
- if tool_name == "request_clarification":
1682
+ if tool_name == "local_tools::request_clarification":
1671
1683
  # Handle clarification...
1672
- return {"final_answer": action.get("clarification_question", "Could you please provide more details?"), "final_scratchpad": current_scratchpad, "tool_calls": tool_calls_this_turn, "sources": sources_this_turn, "clarification_required": True, "error": None}
1673
-
1674
- if tool_name == "final_answer":
1684
+ if isinstance(action, dict):
1685
+ return {"final_answer": action.get("clarification_question", "Could you please provide more details?"), "final_scratchpad": current_scratchpad, "tool_calls": tool_calls_this_turn, "sources": sources_this_turn, "clarification_required": True, "error": None}
1686
+ elif isinstance(action, str):
1687
+ return {"final_answer": action, "final_scratchpad": current_scratchpad, "tool_calls": tool_calls_this_turn, "sources": sources_this_turn, "clarification_required": True, "error": None}
1688
+ else:
1689
+ return {"final_answer": "Could you please provide more details?", "final_scratchpad": current_scratchpad, "tool_calls": tool_calls_this_turn, "sources": sources_this_turn, "clarification_required": True, "error": None}
1690
+ if tool_name == "local_tools::final_answer":
1675
1691
  current_scratchpad += f"\n\n### Step {i+1}: Action\n- **Action:** Decided to formulate the final answer."
1676
1692
  log_event("**Action**: Formulate final answer.", MSG_TYPE.MSG_TYPE_THOUGHT_CHUNK)
1677
1693
  if reasoning_step_id: log_event(f"**Reasoning Step {i+1}/{max_reasoning_steps}**",MSG_TYPE.MSG_TYPE_STEP_END, event_id=reasoning_step_id)
1678
1694
  break
1679
1695
 
1680
1696
  # --- Handle the `put_code_in_buffer` tool specifically ---
1681
- if tool_name == 'put_code_in_buffer':
1697
+ if tool_name == 'local_tools::put_code_in_buffer':
1682
1698
  code_gen_id = log_event(f"Generating code...", MSG_TYPE.MSG_TYPE_STEP_START, metadata={"name": "put_code_in_buffer", "id": "gencode"})
1683
1699
  code_prompt = tool_params.get("prompt", "Generate the requested code.")
1684
1700
 
@@ -1697,7 +1713,7 @@ Provide your response as a single JSON object inside a JSON markdown tag. Use th
1697
1713
  if code_gen_id: log_event(f"Generating code...", MSG_TYPE.MSG_TYPE_TOOL_CALL, metadata={"id": code_gen_id, "result": tool_result})
1698
1714
  if reasoning_step_id: log_event(f"**Reasoning Step {i+1}/{max_reasoning_steps}**", MSG_TYPE.MSG_TYPE_STEP_END, event_id= reasoning_step_id)
1699
1715
  continue # Go to the next reasoning step immediately
1700
- if tool_name == 'view_generated_code':
1716
+ if tool_name == 'local_tools::view_generated_code':
1701
1717
  code_id = tool_params.get("code_id")
1702
1718
  if code_id:
1703
1719
  tool_result = {"status": "success", "code_id": code_id, "generated_code":generated_code_store[code_uuid]}
@@ -1707,7 +1723,7 @@ Provide your response as a single JSON object inside a JSON markdown tag. Use th
1707
1723
  current_scratchpad += f"\n\n### Step {i+1}: Observation\n- **Action:** Called `{tool_name}`\n- **Result:**\n{observation_text}"
1708
1724
  log_event(f"Result from `{tool_name}`:\n```\n{generated_code_store[code_uuid]}\n```\n", MSG_TYPE.MSG_TYPE_TOOL_CALL, metadata={"id": code_gen_id, "result": tool_result})
1709
1725
  continue
1710
- if tool_name == 'refactor_scratchpad':
1726
+ if tool_name == 'local_tools::refactor_scratchpad':
1711
1727
  scratchpad_cleaning_prompt = f"""Enhance this scratchpad content to be more organized and comprehensive. Keep relevant experience information and remove any useless redundancies. Try to log learned things from the context so that you won't make the same mistakes again. Do not remove the main objective information or any crucial information that may be useful for the next iterations. Answer directly with the new scratchpad content without any comments.
1712
1728
  --- YOUR INTERNAL SCRATCHPAD (Work History & Analysis) ---
1713
1729
  {current_scratchpad}
@@ -2961,13 +2977,12 @@ Provide the final aggregated answer in {output_format} format, directly addressi
2961
2977
  callback("Deep analysis complete.", MSG_TYPE.MSG_TYPE_STEP_END)
2962
2978
  return final_output
2963
2979
 
2964
-
2965
- def summarize(
2980
+ def long_context_processing(
2966
2981
  self,
2967
- text_to_summarize: str,
2982
+ text_to_process: str,
2968
2983
  contextual_prompt: Optional[str] = None,
2969
- chunk_size_tokens: int = 1500,
2970
- overlap_tokens: int = 250,
2984
+ chunk_size_tokens: int|None = None,
2985
+ overlap_tokens: int = 0,
2971
2986
  streaming_callback: Optional[Callable] = None,
2972
2987
  **kwargs
2973
2988
  ) -> str:
@@ -2979,7 +2994,7 @@ Provide the final aggregated answer in {output_format} format, directly addressi
2979
2994
  2. **Synthesize:** It then takes all the chunk summaries and performs a final summarization pass to create a single, coherent, and comprehensive summary.
2980
2995
 
2981
2996
  Args:
2982
- text_to_summarize (str): The long text content to be summarized.
2997
+ text_to_process (str): The long text content to be summarized.
2983
2998
  contextual_prompt (Optional[str], optional): A specific instruction to guide the summary's focus.
2984
2999
  For example, "Summarize the text focusing on the financial implications."
2985
3000
  Defaults to None.
@@ -2990,26 +3005,47 @@ Provide the final aggregated answer in {output_format} format, directly addressi
2990
3005
  is not lost at the boundaries. Defaults to 250.
2991
3006
  streaming_callback (Optional[Callable], optional): A callback function to receive real-time updates
2992
3007
  on the process (e.g., which chunk is being processed).
3008
+ It receives a message, a message type, and optional metadata.
2993
3009
  Defaults to None.
2994
3010
  **kwargs: Additional keyword arguments to be passed to the generation method (e.g., temperature, top_p).
2995
3011
 
2996
3012
  Returns:
2997
3013
  str: The final, comprehensive summary of the text.
2998
3014
  """
2999
- if not text_to_summarize.strip():
3015
+ if not text_to_process and len(kwargs.get("images",[]))==0:
3000
3016
  return ""
3001
-
3002
- # Use the binding's tokenizer for accurate chunking
3003
- tokens = self.binding.tokenize(text_to_summarize)
3017
+ if not text_to_process:
3018
+ text_to_process=""
3019
+ tokens = []
3020
+ else:
3021
+ # Use the binding's tokenizer for accurate chunking
3022
+ tokens = self.binding.tokenize(text_to_process)
3023
+ if chunk_size_tokens is None:
3024
+ chunk_size_tokens = self.default_ctx_size//2
3004
3025
 
3005
3026
  if len(tokens) <= chunk_size_tokens:
3006
3027
  if streaming_callback:
3007
- streaming_callback("Text is short enough for a single summary.", MSG_TYPE.MSG_TYPE_STEP)
3028
+ streaming_callback("Text is short enough for a single process.", MSG_TYPE.MSG_TYPE_STEP, {"progress": 0})
3029
+ system_prompt = ("You are a content processor expert.\n"
3030
+ "You perform tasks on the content as requested by the user.\n\n"
3031
+ "--- Content ---\n"
3032
+ f"{text_to_process}\n\n"
3033
+ "** Important **\n"
3034
+ "Strictly adhere to the user prompt.\n"
3035
+ "Do not add comments unless asked to do so.\n"
3036
+ )
3037
+ if "system_prompt" in kwargs:
3038
+ system_prompt += "-- Extra instructions --\n"+ kwargs["system_prompt"] +"\n"
3039
+ del kwargs["system_prompt"]
3040
+ prompt_objective = contextual_prompt or "Provide a comprehensive summary of the content."
3041
+ final_prompt = f"{prompt_objective}"
3008
3042
 
3009
- prompt_objective = contextual_prompt or "Provide a comprehensive summary of the following text."
3010
- final_prompt = f"{prompt_objective}\n\n--- Text to Summarize ---\n{text_to_summarize}"
3043
+ processed_output = self.generate_text(final_prompt, system_prompt=system_prompt, **kwargs)
3011
3044
 
3012
- return self.generate_text(final_prompt, **kwargs)
3045
+ if streaming_callback:
3046
+ streaming_callback("Content processed.", MSG_TYPE.MSG_TYPE_STEP, {"progress": 100})
3047
+
3048
+ return processed_output
3013
3049
 
3014
3050
  # --- Stage 1: Chunking and Independent Summarization ---
3015
3051
  chunks = []
@@ -3021,52 +3057,107 @@ Provide the final aggregated answer in {output_format} format, directly addressi
3021
3057
 
3022
3058
  chunk_summaries = []
3023
3059
 
3060
+ # Total steps include each chunk plus the final synthesis step
3061
+ total_steps = len(chunks) + 1
3062
+
3024
3063
  # Define the prompt for summarizing each chunk
3025
3064
  summarization_objective = contextual_prompt or "Summarize the key points of the following text excerpt."
3026
- chunk_summary_prompt_template = f"{summarization_objective}\n\n--- Text Excerpt ---\n{{chunk_text}}"
3065
+ system_prompt = ("You are a sequential document processing agent.\n"
3066
+ "The process is done in two phases:\n"
3067
+ "** Phase1 : **\n"
3068
+ "Sequencially extracting information from the text chunks and adding them to the scratchpad.\n"
3069
+ "** Phase2: **\n"
3070
+ "Synthesizing a comprehensive Response using the scratchpad content given the objective formatting instructions if applicable.\n"
3071
+ "We are now performing ** Phase 1 **, and we are processing chunk number {{chunk_id}}.\n"
3072
+ "Your job is to extract information from the current chunk given previous chunks extracted information placed in scratchpad as well as the current chunk content.\n"
3073
+ "Add the information to the scratchpad while strictly adhering to the Global objective extraction instructions:\n"
3074
+ "-- Sequencial Scratchpad --\n"
3075
+ "{{scratchpad}}\n"
3076
+ "** Important **\n"
3077
+ "Respond only with the extracted information from the current chunk without repeating things that are already in the scratchpad.\n"
3078
+ "Strictly adhere to the Global objective content for the extraction phase.\n"
3079
+ "Do not add comments.\n"
3080
+ )
3081
+ if "system_prompt" in kwargs:
3082
+ system_prompt += "-- Extra instructions --\n"+ kwargs["system_prompt"] +"\n"
3083
+ del kwargs["system_prompt"]
3084
+ chunk_summary_prompt_template = f"--- Global objective ---\n{summarization_objective}\n\n--- Text Excerpt ---\n{{chunk_text}}"
3027
3085
 
3028
3086
  for i, chunk in enumerate(chunks):
3087
+ progress_before = (i / total_steps) * 100
3029
3088
  if streaming_callback:
3030
- streaming_callback(f"Summarizing chunk {i + 1} of {len(chunks)}...", MSG_TYPE.MSG_TYPE_STEP_START, {"id": f"chunk_{i+1}"})
3089
+ streaming_callback(
3090
+ f"Processing chunk {i + 1} of {len(chunks)}...",
3091
+ MSG_TYPE.MSG_TYPE_STEP_START,
3092
+ {"id": f"chunk_{i+1}", "progress": progress_before}
3093
+ )
3031
3094
 
3032
3095
  prompt = chunk_summary_prompt_template.format(chunk_text=chunk)
3033
-
3096
+ processed_system_prompt = system_prompt.format(chunk_id=i,scratchpad="\n\n---\n\n".join(chunk_summaries))
3034
3097
  try:
3035
3098
  # Generate summary for the current chunk
3036
- chunk_summary = self.generate_text(prompt, **kwargs)
3099
+ chunk_summary = self.generate_text(prompt, system_prompt=processed_system_prompt, **kwargs)
3037
3100
  chunk_summaries.append(chunk_summary)
3101
+
3102
+ progress_after = ((i + 1) / total_steps) * 100
3038
3103
  if streaming_callback:
3039
- streaming_callback(f"Chunk {i + 1} summarized.", MSG_TYPE.MSG_TYPE_STEP_END, {"id": f"chunk_{i+1}", "summary_snippet": chunk_summary[:100]})
3104
+ streaming_callback(
3105
+ f"Chunk {i + 1} processed. Progress: {progress_after:.0f}%",
3106
+ MSG_TYPE.MSG_TYPE_STEP_END,
3107
+ {"id": f"chunk_{i+1}", "output_snippet": chunk_summary[:100], "progress": progress_after}
3108
+ )
3040
3109
  except Exception as e:
3041
3110
  trace_exception(e)
3042
3111
  if streaming_callback:
3043
- streaming_callback(f"Failed to summarize chunk {i+1}: {e}", MSG_TYPE.MSG_TYPE_EXCEPTION)
3112
+ streaming_callback(f"Failed to process chunk {i+1}: {e}", MSG_TYPE.MSG_TYPE_EXCEPTION)
3044
3113
  # Still add a placeholder to not break the chain
3045
- chunk_summaries.append(f"[Error summarizing chunk {i+1}]")
3114
+ chunk_summaries.append(f"[Error processing chunk {i+1}]")
3046
3115
 
3047
3116
  # --- Stage 2: Final Synthesis of All Chunk Summaries ---
3117
+ progress_before_synthesis = (len(chunks) / total_steps) * 100
3048
3118
  if streaming_callback:
3049
- streaming_callback("Synthesizing all chunk summaries into a final version...", MSG_TYPE.MSG_TYPE_STEP_START, {"id": "final_synthesis"})
3119
+ streaming_callback(
3120
+ "Processing the scratchpad content into a final version...",
3121
+ MSG_TYPE.MSG_TYPE_STEP_START,
3122
+ {"id": "final_synthesis", "progress": progress_before_synthesis}
3123
+ )
3050
3124
 
3051
3125
  combined_summaries = "\n\n---\n\n".join(chunk_summaries)
3052
3126
 
3053
3127
  # Define the prompt for the final synthesis
3054
3128
  synthesis_objective = contextual_prompt or "Create a single, final, coherent, and comprehensive summary."
3129
+ system_prompt = ("You are a sequential document processing agent.\n"
3130
+ "The process is done in two phases:\n"
3131
+ "** Phase1 : **\n"
3132
+ "Sequencially extracting information from the text chunks and adding them to the scratchpad.\n"
3133
+ "** Phase2: **\n"
3134
+ "Synthesizing a comprehensive Response using the scratchpad content given the objective formatting instructions if applicable.\n"
3135
+ "\n"
3136
+ "We are now performing ** Phase 2 **.\n"
3137
+ "Your job is to use the extracted information to fulfill the user prompt objectives.\n"
3138
+ "Make sure you respect the user formatting if provided and if not, then use markdown output format."
3139
+ "-- Sequencial Scratchpad --\n"
3140
+ f"{combined_summaries}\n"
3141
+ "** Important **\n"
3142
+ "Respond only with the requested task without extra comments unless told to.\n"
3143
+ "Strictly adhere to the Global objective content for the extraction phase.\n"
3144
+ "Do not add comments.\n"
3145
+ )
3055
3146
  final_synthesis_prompt = (
3056
- "You are a master synthesizer. You will be given a series of partial summaries from a long document. "
3057
- f"Your task is to synthesize them into one high-quality summary. {synthesis_objective}\n\n"
3058
- "Please remove any redundancy and ensure a smooth, logical flow.\n\n"
3059
- "--- Collection of Summaries ---\n"
3060
- f"{combined_summaries}\n\n"
3061
- "--- Final Comprehensive Summary ---"
3147
+ f"--- Global objective ---\n{synthesis_objective}\n\n"
3148
+ "--- Final Response ---"
3062
3149
  )
3063
3150
 
3064
- final_summary = self.generate_text(final_synthesis_prompt, **kwargs)
3151
+ final_answer = self.generate_text(final_synthesis_prompt, system_prompt=system_prompt, **kwargs)
3065
3152
 
3066
3153
  if streaming_callback:
3067
- streaming_callback("Final summary synthesized.", MSG_TYPE.MSG_TYPE_STEP_END, {"id": "final_synthesis"})
3154
+ streaming_callback(
3155
+ "Final summary synthesized.",
3156
+ MSG_TYPE.MSG_TYPE_STEP_END,
3157
+ {"id": "final_synthesis", "progress": 100}
3158
+ )
3068
3159
 
3069
- return final_summary.strip()
3160
+ return final_answer.strip()
3070
3161
 
3071
3162
  def chunk_text(text, tokenizer, detokenizer, chunk_size, overlap, use_separators=True):
3072
3163
  """