From a830b1721affb59858cf59d05e54e6859d325b3b Mon Sep 17 00:00:00 2001 From: Aymeric Date: Mon, 9 Dec 2024 18:34:29 +0100 Subject: [PATCH] More robust log step class, and new examples --- agents/agent_types.py | 2 +- agents/agents.py | 377 ++++++++++------------------------- agents/monitoring.py | 6 +- agents/tools.py | 103 ++++++++++ agents/utils.py | 73 +++++++ examples/agent_planning.py | 36 ++++ examples/agent_with_tools.py | 4 +- examples/local_smollm.py | 116 +++++++++++ poetry.lock | 347 +++++++++++++++++++++++++++++++- pyproject.toml | 4 + 10 files changed, 787 insertions(+), 281 deletions(-) create mode 100644 examples/agent_planning.py create mode 100644 examples/local_smollm.py diff --git a/agents/agent_types.py b/agents/agent_types.py index 5e3169c..05bf6e3 100644 --- a/agents/agent_types.py +++ b/agents/agent_types.py @@ -156,7 +156,7 @@ class AgentImage(AgentType, ImageType): directory = tempfile.mkdtemp() self._path = os.path.join(directory, str(uuid.uuid4()) + ".png") - img.save(self._path) + img.save(self._path, format="png") return self._path diff --git a/agents/agents.py b/agents/agents.py index 68db965..5749b8b 100644 --- a/agents/agents.py +++ b/agents/agents.py @@ -15,16 +15,14 @@ # See the License for the specific language governing permissions and # limitations under the License. import json -import logging import re import time from typing import Any, Callable, Dict, List, Optional, Tuple, Union -import rich -from rich import markdown as rich_markdown +from dataclasses import dataclass from transformers.utils import is_torch_available import logging -from .utils import console +from .utils import console, parse_code_blob, parse_json_tool_call from .agent_types import AgentAudio, AgentImage from .default_tools import BASE_PYTHON_TOOLS, FinalAnswerTool, setup_default_tools from .llm_engine import HfApiEngine, MessageRole @@ -47,200 +45,15 @@ from .tools import ( Tool, get_tool_description_with_args, load_tool, + Toolbox, ) - -def parse_json_blob(json_blob: str) -> Dict[str, str]: - try: - first_accolade_index = json_blob.find("{") - last_accolade_index = [a.start() for a in list(re.finditer("}", json_blob))][-1] - json_blob = json_blob[first_accolade_index : last_accolade_index + 1].replace('\\"', "'") - json_data = json.loads(json_blob, strict=False) - return json_data - except json.JSONDecodeError as e: - place = e.pos - if json_blob[place - 1 : place + 2] == "},\n": - raise ValueError( - "JSON is invalid: you probably tried to provide multiple tool calls in one action. PROVIDE ONLY ONE TOOL CALL." - ) - raise ValueError( - f"The JSON blob you used is invalid due to the following error: {e}.\n" - f"JSON blob was: {json_blob}, decoding failed on that specific part of the blob:\n" - f"'{json_blob[place-4:place+5]}'." - ) - except Exception as e: - raise ValueError(f"Error in parsing the JSON blob: {e}") - - -def parse_code_blob(code_blob: str) -> str: - try: - pattern = r"```(?:py|python)?\n(.*?)\n```" - match = re.search(pattern, code_blob, re.DOTALL) - return match.group(1).strip() - except Exception as e: - raise ValueError( - f""" -The code blob you used is invalid: due to the following error: {e} -This means that the regex pattern {pattern} was not respected: make sure to include code with the correct pattern, for instance: -Thoughts: Your thoughts -Code: -```py -# Your python code here -```""" - ) - - -def parse_json_tool_call(json_blob: str) -> Tuple[str, Dict[str, str]]: - json_blob = json_blob.replace("```json", "").replace("```", "") - tool_call = parse_json_blob(json_blob) - if "action" in tool_call and "action_input" in tool_call: - return tool_call["action"], tool_call["action_input"] - elif "action" in tool_call: - return tool_call["action"], None - else: - missing_keys = [key for key in ['action', 'action_input'] if key not in tool_call] - error_msg = f"Missing keys: {missing_keys} in blob {tool_call}" - console.print(f"[bold red]{error_msg}[/bold red]") - raise ValueError(error_msg) - - -def parse_text_tool_call(text: str) -> Tuple[str, Union[str, Dict[str, str]]]: - """ - Expects a text in the format: 'Action:', 'Action input:', 'Observation:'. 'Action input:' contains a json string with input arguments. - """ - try: - if "Observation:" in text: - text = text.split("Observation:")[0] - if "Action:" in text: - text = text.split("Action:")[1] - tool_name, tool_input = text.split("Action input:") - if "{" in tool_input: - tool_input = parse_json_blob(tool_input) - else: - tool_input = tool_input.strip().replace('"', "") - return tool_name.strip().replace('"', "").replace("\\", ""), tool_input - except Exception as e: - raise ValueError( - f"Error in parsing the text tool call: {e}. Be sure to provide the correct format. DO NOT repeat your previous incorrect tool call." - ) - - -def to_text(input: Union[List[Dict[str, str]], Dict[str, str], str]) -> str: - if isinstance(input, list): - return "\n".join([m["content"] for m in input]) - elif isinstance(input, dict): - return input["content"] - else: - return input +LENGTH_TRUNCATE_REPORTS = 10000 HUGGINGFACE_DEFAULT_TOOLS = {} _tools_are_initialized = False - -class Toolbox: - """ - The toolbox contains all tools that the agent can perform operations with, as well as a few methods to - manage them. - - Args: - tools (`List[Tool]`): - The list of tools to instantiate the toolbox with - add_base_tools (`bool`, defaults to `False`, *optional*, defaults to `False`): - Whether to add the tools available within `transformers` to the toolbox. - """ - - def __init__(self, tools: List[Tool], add_base_tools: bool = False): - self._tools = {tool.name: tool for tool in tools} - if add_base_tools: - self.add_base_tools() - # self._load_tools_if_needed() - - def add_base_tools(self, add_python_interpreter: bool = False): - global _tools_are_initialized - global HUGGINGFACE_DEFAULT_TOOLS - if not _tools_are_initialized: - HUGGINGFACE_DEFAULT_TOOLS = setup_default_tools() - _tools_are_initialized = True - for tool in HUGGINGFACE_DEFAULT_TOOLS.values(): - if tool.name != "python_interpreter" or add_python_interpreter: - self.add_tool(tool) - # self._load_tools_if_needed() - - @property - def tools(self) -> Dict[str, Tool]: - """Get all tools currently in the toolbox""" - return self._tools - - def show_tool_descriptions(self, tool_description_template: str = None) -> str: - """ - Returns the description of all tools in the toolbox - - Args: - tool_description_template (`str`, *optional*): - The template to use to describe the tools. If not provided, the default template will be used. - """ - return "\n".join( - [get_tool_description_with_args(tool, tool_description_template) for tool in self._tools.values()] - ) - - def add_tool(self, tool: Tool): - """ - Adds a tool to the toolbox - - Args: - tool (`Tool`): - The tool to add to the toolbox. - """ - if tool.name in self._tools: - raise KeyError(f"Error: tool '{tool.name}' already exists in the toolbox.") - self._tools[tool.name] = tool - - def remove_tool(self, tool_name: str): - """ - Removes a tool from the toolbox - - Args: - tool_name (`str`): - The tool to remove from the toolbox. - """ - if tool_name not in self._tools: - raise KeyError( - f"Error: tool {tool_name} not found in toolbox for removal, should be instead one of {list(self._tools.keys())}." - ) - del self._tools[tool_name] - - def update_tool(self, tool: Tool): - """ - Updates a tool in the toolbox according to its name. - - Args: - tool (`Tool`): - The tool to update to the toolbox. - """ - if tool.name not in self._tools: - raise KeyError( - f"Error: tool {tool.name} not found in toolbox for update, should be instead one of {list(self._tools.keys())}." - ) - self._tools[tool.name] = tool - - def clear_toolbox(self): - """Clears the toolbox""" - self._tools = {} - - # def _load_tools_if_needed(self): - # for name, tool in self._tools.items(): - # if not isinstance(tool, Tool): - # task_or_repo_id = tool.task if tool.repo_id is None else tool.repo_id - # self._tools[name] = load_tool(task_or_repo_id) - - def __repr__(self): - toolbox_description = "Toolbox contents:\n" - for tool in self._tools.values(): - toolbox_description += f"\t{tool.name}: {tool.description}\n" - return toolbox_description - - class AgentError(Exception): """Base class for other agent-related exceptions""" @@ -274,6 +87,25 @@ class AgentGenerationError(AgentError): pass +@dataclass +class ActionStep: + tool_call: str | None = None + start_time: float | None = None + end_time: float | None = None + iteration: int | None = None + final_answer: Any = None + error: AgentError | None = None + step_duration: float | None = None + +@dataclass +class PlanningStep: + plan: str + facts: str + +@dataclass +class TaskStep: + system_prompt: str + task: str def format_prompt_with_tools(toolbox: Toolbox, prompt_template: str, tool_description_template: str) -> str: tool_descriptions = toolbox.show_tool_descriptions(tool_description_template) @@ -392,7 +224,7 @@ class Agent: self.system_prompt = format_prompt_with_imports( self.system_prompt, list(set(LIST_SAFE_MODULES) | set(self.authorized_imports)) ) - self.logs = [{"system_prompt": self.system_prompt, "task": self.task}] + self.logs = [TaskStep(system_prompt=self.system_prompt, task=self.task)] console.rule("New task", characters='=') console.print(self.task) @@ -401,55 +233,58 @@ class Agent: Reads past llm_outputs, actions, and observations or errors from the logs into a series of messages that can be used as input to the LLM. """ - prompt_message = {"role": MessageRole.SYSTEM, "content": self.logs[0]["system_prompt"]} + prompt_message = {"role": MessageRole.SYSTEM, "content": self.logs[0].system_prompt} task_message = { "role": MessageRole.USER, - "content": "Task: " + self.logs[0]["task"], + "content": "Task: " + self.logs[0].task, } if summary_mode: memory = [task_message] else: memory = [prompt_message, task_message] for i, step_log in enumerate(self.logs[1:]): - if "llm_output" in step_log and not summary_mode: - thought_message = {"role": MessageRole.ASSISTANT, "content": step_log["llm_output"].strip()} - memory.append(thought_message) - if "facts" in step_log: + + if isinstance(step_log, PlanningStep): thought_message = { "role": MessageRole.ASSISTANT, - "content": "[FACTS LIST]:\n" + step_log["facts"].strip(), + "content": "[FACTS LIST]:\n" + step_log.facts.strip(), } memory.append(thought_message) - if "plan" in step_log and not summary_mode: - thought_message = {"role": MessageRole.ASSISTANT, "content": "[PLAN]:\n" + step_log["plan"].strip()} - memory.append(thought_message) + if not summary_mode: + thought_message = {"role": MessageRole.ASSISTANT, "content": "[PLAN]:\n" + step_log.plan.strip()} + memory.append(thought_message) - if "tool_call" in step_log and summary_mode: - tool_call_message = { - "role": MessageRole.ASSISTANT, - "content": f"[STEP {i} TOOL CALL]: " + str(step_log["tool_call"]).strip(), - } - memory.append(tool_call_message) - - if "task" in step_log: - tool_call_message = { + elif isinstance(step_log, TaskStep): + task_message = { "role": MessageRole.USER, - "content": "New task:\n" + step_log["task"], + "content": "New task:\n" + step_log.task, } - memory.append(tool_call_message) + memory.append(task_message) - if "error" in step_log or "observation" in step_log: - if "error" in step_log: - message_content = ( - f"[OUTPUT OF STEP {i}] -> Error:\n" - + str(step_log["error"]) - + "\nNow let's retry: take care not to repeat previous errors! If you have retried several times, try a completely different approach.\n" - ) - elif "observation" in step_log: - message_content = f"[OUTPUT OF STEP {i}] -> Observation:\n{step_log['observation']}" - tool_response_message = {"role": MessageRole.TOOL_RESPONSE, "content": message_content} - memory.append(tool_response_message) + elif isinstance(step_log, ActionStep): + if step_log.llm_output is not None and not summary_mode: + thought_message = {"role": MessageRole.ASSISTANT, "content": step_log.llm_output.strip()} + memory.append(thought_message) + + if step_log.tool_call is not None and summary_mode: + tool_call_message = { + "role": MessageRole.ASSISTANT, + "content": f"[STEP {i} TOOL CALL]: " + str(step_log.tool_call).strip(), + } + memory.append(tool_call_message) + + if step_log.error is not None or step_log.observation is not None: + if step_log.error is not None: + message_content = ( + f"[OUTPUT OF STEP {i}] -> Error:\n" + + str(step_log.error) + + "\nNow let's retry: take care not to repeat previous errors! If you have retried several times, try a completely different approach.\n" + ) + elif step_log.observation is not None: + message_content = f"[OUTPUT OF STEP {i}] -> Observation:\n{step_log.observation}" + tool_response_message = {"role": MessageRole.TOOL_RESPONSE, "content": message_content} + memory.append(tool_response_message) return memory @@ -742,7 +577,7 @@ class ReactAgent(Agent): if reset: self.initialize_for_run() else: - self.logs.append({"task": task}) + self.logs.append(TaskStep(task=task)) if stream: return self.stream_run(task) else: @@ -756,31 +591,31 @@ class ReactAgent(Agent): iteration = 0 while final_answer is None and iteration < self.max_iterations: step_start_time = time.time() - step_log_entry = {"iteration": iteration, "start_time": step_start_time} + step_log = ActionStep(iteration=iteration, start_time=step_start_time) try: - self.step(step_log_entry) - if "final_answer" in step_log_entry: - final_answer = step_log_entry["final_answer"] + self.step(step_log) + if step_log.final_answer is not None: + final_answer = step_log.final_answer except AgentError as e: - step_log_entry["error"] = e + step_log.error = e finally: - step_end_time = time.time() - step_log_entry["step_end_time"] = step_end_time - step_log_entry["step_duration"] = step_end_time - step_start_time - self.logs.append(step_log_entry) + step_log.step_end_time = time.time() + step_log.step_duration = step_log.step_end_time - step_start_time + self.logs.append(step_log) for callback in self.step_callbacks: - callback(step_log_entry) + callback(step_log) iteration += 1 - yield step_log_entry + yield step_log if final_answer is None and iteration == self.max_iterations: error_message = "Reached max iterations." - final_step_log = {"error": AgentMaxIterationsError(error_message)} - self.logs.append(final_step_log) console.print(f"[bold red]{error_message}") + final_step_log = ActionStep(error=AgentMaxIterationsError(error_message)) + self.logs.append(final_step_log) final_answer = self.provide_final_answer(task) - final_step_log["final_answer"] = final_answer - final_step_log["step_duration"] = 0 + final_step_log.final_answer = final_answer + final_step_log.step_end_time = time.time() + final_step_log.step_duration = step_log.step_end_time - step_start_time for callback in self.step_callbacks: callback(final_step_log) yield final_step_log @@ -795,32 +630,32 @@ class ReactAgent(Agent): iteration = 0 while final_answer is None and iteration < self.max_iterations: step_start_time = time.time() - step_log_entry = {"iteration": iteration, "start_time": step_start_time} + step_log = ActionStep(iteration=iteration, start_time=step_start_time) try: if self.planning_interval is not None and iteration % self.planning_interval == 0: self.planning_step(task, is_first_step=(iteration == 0), iteration=iteration) - self.step(step_log_entry) - if "final_answer" in step_log_entry: - final_answer = step_log_entry["final_answer"] + self.step(step_log) + if step_log.final_answer is not None: + final_answer = step_log.final_answer except AgentError as e: - step_log_entry["error"] = e + step_log.error = e finally: step_end_time = time.time() - step_log_entry["step_end_time"] = step_end_time - step_log_entry["step_duration"] = step_end_time - step_start_time - self.logs.append(step_log_entry) + step_log.step_end_time = step_end_time + step_log.step_duration = step_end_time - step_start_time + self.logs.append(step_log) for callback in self.step_callbacks: - callback(step_log_entry) + callback(step_log) iteration += 1 if final_answer is None and iteration == self.max_iterations: error_message = "Reached max iterations." - final_step_log = {"error": AgentMaxIterationsError(error_message)} + final_step_log = ActionStep(error=AgentMaxIterationsError(error_message)) self.logs.append(final_step_log) console.print(f"[bold red]{error_message}") final_answer = self.provide_final_answer(task) - final_step_log["final_answer"] = final_answer - final_step_log["step_duration"] = 0 + final_step_log.final_answer = final_answer + final_step_log.step_duration = 0 for callback in self.step_callbacks: callback(final_step_log) @@ -875,7 +710,7 @@ Now begin!""", ``` {answer_facts} ```""".strip() - self.logs.append({"plan": final_plan_redaction, "facts": final_facts_redaction}) + self.logs.append(PlanningStep(plan=final_plan_redaction, facts=final_facts_redaction)) console.rule("[orange]Initial plan") console.print(final_plan_redaction) else: # update plan @@ -921,8 +756,8 @@ Now begin!""", ``` {facts_update} ```""" - self.logs.append({"plan": final_plan_redaction, "facts": final_facts_redaction}) - console.rule("Updated plan") + self.logs.append(PlanningStep(plan=final_plan_redaction, facts=final_facts_redaction)) + console.rule("[orange]Updated plan") console.print(final_plan_redaction) @@ -959,7 +794,7 @@ class ReactJsonAgent(ReactAgent): **kwargs, ) - def step(self, log_entry: Dict[str, Any]): + def step(self, log_entry: ActionStep): """ Perform one step in the ReAct framework: the agent thinks, acts, and observes the result. The errors are raised here, they are caught and logged in the run() method. @@ -970,7 +805,7 @@ class ReactJsonAgent(ReactAgent): console.rule("New step") # Add new step in logs - log_entry["agent_memory"] = agent_memory.copy() + log_entry.agent_memory = agent_memory.copy() if self.verbose: console.rule("Calling LLM with this last message:") @@ -985,7 +820,7 @@ class ReactJsonAgent(ReactAgent): raise AgentGenerationError(f"Error in generating llm output: {e}.") console.rule("===== Output message of the LLM: =====") console.print(llm_output) - log_entry["llm_output"] = llm_output + log_entry.llm_output = llm_output # Parse console.rule("===== Extracting action =====") @@ -996,8 +831,8 @@ class ReactJsonAgent(ReactAgent): except Exception as e: raise AgentParsingError(f"Could not parse the given action: {e}.") - log_entry["rationale"] = rationale - log_entry["tool_call"] = {"tool_name": tool_name, "tool_arguments": arguments} + log_entry.rationale = rationale + log_entry.tool_call = {"tool_name": tool_name, "tool_arguments": arguments} # Execute console.print("=== Agent thoughts:") @@ -1015,7 +850,7 @@ class ReactJsonAgent(ReactAgent): answer = arguments else: answer = arguments - log_entry["final_answer"] = answer + log_entry.final_answer = answer return answer else: if arguments is None: @@ -1033,7 +868,7 @@ class ReactJsonAgent(ReactAgent): updated_information = f"Stored '{observation_name}' in memory." else: updated_information = str(observation).strip() - log_entry["observation"] = updated_information + log_entry.observation = updated_information return log_entry @@ -1088,7 +923,7 @@ class ReactCodeAgent(ReactAgent): console.rule("New step") # Add new step in logs - log_entry["agent_memory"] = agent_memory.copy() + log_entry.agent_memory = agent_memory.copy() if self.verbose: console.print("===== Calling LLM with these last messages: =====") @@ -1105,7 +940,7 @@ class ReactCodeAgent(ReactAgent): if self.verbose: console.rule("Output message of the LLM:") console.print(llm_output) - log_entry["llm_output"] = llm_output + log_entry.llm_output = llm_output # Parse try: @@ -1120,8 +955,8 @@ class ReactCodeAgent(ReactAgent): error_msg = f"Error in code parsing: {e}. Make sure to provide correct code" raise AgentParsingError(error_msg) - log_entry["rationale"] = rationale - log_entry["tool_call"] = {"tool_name": "code interpreter", "tool_arguments": code_action} + log_entry.rationale = rationale + log_entry.tool_call = {"tool_name": "code interpreter", "tool_arguments": code_action} # Execute self.log_rationale_code_action(rationale, code_action) @@ -1145,8 +980,8 @@ class ReactCodeAgent(ReactAgent): if result is not None: console.print("Last output from code snippet:") console.print(str(result)) - observation += "Last output from code snippet:\n" + str(result)[:100000] - log_entry["observation"] = observation + observation += "Last output from code snippet:\n" + str(result)[:LENGTH_TRUNCATE_REPORTS] + log_entry.observation = observation except Exception as e: error_msg = f"Code execution failed due to the following error:\n{str(e)}" if "'dict' object has no attribute 'read'" in str(e): @@ -1156,13 +991,9 @@ class ReactCodeAgent(ReactAgent): if line[: len("final_answer")] == "final_answer": console.print("Final answer:") console.print(f"[bold]{result}") - log_entry["final_answer"] = result + log_entry.final_answer = result return result - -LENGTH_TRUNCATE_REPORTS = 1000 - - class ManagedAgent: def __init__(self, agent, name, description, additional_prompting=None, provide_run_summary=False): self.agent = agent diff --git a/agents/monitoring.py b/agents/monitoring.py index 8675c2d..9e579e8 100644 --- a/agents/monitoring.py +++ b/agents/monitoring.py @@ -102,7 +102,7 @@ class Monitor: self.total_output_token_count = 0 def update_metrics(self, step_log): - step_duration = step_log["step_duration"] + step_duration = step_log.step_duration self.step_durations.append(step_duration) console.print(f"Step {len(self.step_durations)}:") console.print(f"- Time taken: {step_duration:.2f} seconds") @@ -110,6 +110,6 @@ class Monitor: if getattr(self.tracked_llm_engine, "last_input_token_count", None) is not None: self.total_input_token_count += self.tracked_llm_engine.last_input_token_count self.total_output_token_count += self.tracked_llm_engine.last_output_token_count - console.print(f"- Input tokens: {self.total_input_token_count}") - console.print(f"- Output tokens: {self.total_output_token_count}") + console.print(f"- Input tokens: {self.total_input_token_count:,}") + console.print(f"- Output tokens: {self.total_output_token_count:,}") diff --git a/agents/tools.py b/agents/tools.py index 1c2fab7..bacb2b7 100644 --- a/agents/tools.py +++ b/agents/tools.py @@ -1000,3 +1000,106 @@ def tool(tool_function: Callable) -> Tool: SpecificTool.__name__ = class_name return SpecificTool() + + +class Toolbox: + """ + The toolbox contains all tools that the agent can perform operations with, as well as a few methods to + manage them. + + Args: + tools (`List[Tool]`): + The list of tools to instantiate the toolbox with + add_base_tools (`bool`, defaults to `False`, *optional*, defaults to `False`): + Whether to add the tools available within `transformers` to the toolbox. + """ + + def __init__(self, tools: List[Tool], add_base_tools: bool = False): + self._tools = {tool.name: tool for tool in tools} + if add_base_tools: + self.add_base_tools() + # self._load_tools_if_needed() + + def add_base_tools(self, add_python_interpreter: bool = False): + global _tools_are_initialized + global HUGGINGFACE_DEFAULT_TOOLS + if not _tools_are_initialized: + HUGGINGFACE_DEFAULT_TOOLS = setup_default_tools() + _tools_are_initialized = True + for tool in HUGGINGFACE_DEFAULT_TOOLS.values(): + if tool.name != "python_interpreter" or add_python_interpreter: + self.add_tool(tool) + # self._load_tools_if_needed() + + @property + def tools(self) -> Dict[str, Tool]: + """Get all tools currently in the toolbox""" + return self._tools + + def show_tool_descriptions(self, tool_description_template: str = None) -> str: + """ + Returns the description of all tools in the toolbox + + Args: + tool_description_template (`str`, *optional*): + The template to use to describe the tools. If not provided, the default template will be used. + """ + return "\n".join( + [get_tool_description_with_args(tool, tool_description_template) for tool in self._tools.values()] + ) + + def add_tool(self, tool: Tool): + """ + Adds a tool to the toolbox + + Args: + tool (`Tool`): + The tool to add to the toolbox. + """ + if tool.name in self._tools: + raise KeyError(f"Error: tool '{tool.name}' already exists in the toolbox.") + self._tools[tool.name] = tool + + def remove_tool(self, tool_name: str): + """ + Removes a tool from the toolbox + + Args: + tool_name (`str`): + The tool to remove from the toolbox. + """ + if tool_name not in self._tools: + raise KeyError( + f"Error: tool {tool_name} not found in toolbox for removal, should be instead one of {list(self._tools.keys())}." + ) + del self._tools[tool_name] + + def update_tool(self, tool: Tool): + """ + Updates a tool in the toolbox according to its name. + + Args: + tool (`Tool`): + The tool to update to the toolbox. + """ + if tool.name not in self._tools: + raise KeyError( + f"Error: tool {tool.name} not found in toolbox for update, should be instead one of {list(self._tools.keys())}." + ) + self._tools[tool.name] = tool + + def clear_toolbox(self): + """Clears the toolbox""" + self._tools = {} + + # def _load_tools_if_needed(self): + # for name, tool in self._tools.items(): + # if not isinstance(tool, Tool): + # task_or_repo_id = tool.task if tool.repo_id is None else tool.repo_id + # self._tools[name] = load_tool(task_or_repo_id) + + def __repr__(self): + toolbox_description = "Toolbox contents:\n" + for tool in self._tools.values(): + toolbox_description += f"\t{tool.name}: {tool.description}\n" + return toolbox_description diff --git a/agents/utils.py b/agents/utils.py index 38f2f78..f0de99c 100644 --- a/agents/utils.py +++ b/agents/utils.py @@ -1,3 +1,22 @@ +#!/usr/bin/env python +# coding=utf-8 + +# Copyright 2023 The HuggingFace Inc. team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import json +import re +from typing import Tuple, Dict from transformers.utils.import_utils import _is_package_available @@ -8,3 +27,57 @@ def is_pygments_available(): from rich.console import Console console = Console() + + +def parse_json_blob(json_blob: str) -> Dict[str, str]: + try: + first_accolade_index = json_blob.find("{") + last_accolade_index = [a.start() for a in list(re.finditer("}", json_blob))][-1] + json_blob = json_blob[first_accolade_index : last_accolade_index + 1].replace('\\"', "'") + json_data = json.loads(json_blob, strict=False) + return json_data + except json.JSONDecodeError as e: + place = e.pos + if json_blob[place - 1 : place + 2] == "},\n": + raise ValueError( + "JSON is invalid: you probably tried to provide multiple tool calls in one action. PROVIDE ONLY ONE TOOL CALL." + ) + raise ValueError( + f"The JSON blob you used is invalid due to the following error: {e}.\n" + f"JSON blob was: {json_blob}, decoding failed on that specific part of the blob:\n" + f"'{json_blob[place-4:place+5]}'." + ) + except Exception as e: + raise ValueError(f"Error in parsing the JSON blob: {e}") + + +def parse_code_blob(code_blob: str) -> str: + try: + pattern = r"```(?:py|python)?\n(.*?)\n```" + match = re.search(pattern, code_blob, re.DOTALL) + return match.group(1).strip() + except Exception as e: + raise ValueError( + f""" +The code blob you used is invalid: due to the following error: {e} +This means that the regex pattern {pattern} was not respected: make sure to include code with the correct pattern, for instance: +Thoughts: Your thoughts +Code: +```py +# Your python code here +```""" + ) + + +def parse_json_tool_call(json_blob: str) -> Tuple[str, Dict[str, str]]: + json_blob = json_blob.replace("```json", "").replace("```", "") + tool_call = parse_json_blob(json_blob) + if "action" in tool_call and "action_input" in tool_call: + return tool_call["action"], tool_call["action_input"] + elif "action" in tool_call: + return tool_call["action"], None + else: + missing_keys = [key for key in ['action', 'action_input'] if key not in tool_call] + error_msg = f"Missing keys: {missing_keys} in blob {tool_call}" + console.print(f"[bold red]{error_msg}[/bold red]") + raise ValueError(error_msg) \ No newline at end of file diff --git a/examples/agent_planning.py b/examples/agent_planning.py new file mode 100644 index 0000000..3876e21 --- /dev/null +++ b/examples/agent_planning.py @@ -0,0 +1,36 @@ +from agents import load_tool, ReactCodeAgent, ReactJsonAgent, HfApiEngine +from agents.default_tools import PythonInterpreterTool + +# Import tool from Hub +image_generation_tool = load_tool("m-ric/text-to-image", cache=False) + +from agents.search import DuckDuckGoSearchTool + +search_tool = DuckDuckGoSearchTool() + +llm_engine = HfApiEngine("Qwen/Qwen2.5-72B-Instruct") + +agent = ReactCodeAgent(tools=[search_tool], llm_engine=llm_engine, planning_interval=3) + +# Run it! +print("Let's run the Code agent:") + +result = agent.run( + "How long would a cheetah at full speed take to run the length of Pont Alexandre III?", +) + +print("RESULT:", result) + + +code_tool = PythonInterpreterTool() + +agent = ReactJsonAgent(tools=[search_tool, code_tool], llm_engine=llm_engine, planning_interval=3) + +print("====================") +print("====================") +print("Now let's run the JSON agent:") +result = agent.run( + "How long would a cheetah at full speed take to run the length of Pont Alexandre III?", +) + +print("RESULT:", result) diff --git a/examples/agent_with_tools.py b/examples/agent_with_tools.py index dbb7a24..93a07f0 100644 --- a/examples/agent_with_tools.py +++ b/examples/agent_with_tools.py @@ -1,11 +1,9 @@ from agents import load_tool, ReactCodeAgent, HfApiEngine +from agents.search import DuckDuckGoSearchTool # Import tool from Hub image_generation_tool = load_tool("m-ric/text-to-image", cache=False) -# Import tool from LangChain -from agents.search import DuckDuckGoSearchTool - search_tool = DuckDuckGoSearchTool() llm_engine = HfApiEngine("Qwen/Qwen2.5-72B-Instruct") diff --git a/examples/local_smollm.py b/examples/local_smollm.py new file mode 100644 index 0000000..fcb81f0 --- /dev/null +++ b/examples/local_smollm.py @@ -0,0 +1,116 @@ +from agents.llm_engine import TransformersEngine +from agents import CodeAgent, ReactJsonAgent + +import requests +from datetime import datetime + +model_repo="andito/SmolLM2-1.7B-Instruct-F16-GGUF" +model_filename="smollm2-1.7b-8k-dpo-f16.gguf" + +import random +from llama_cpp import Llama + +model = Llama.from_pretrained( + repo_id=model_repo, + filename=model_filename, + n_ctx=8192, + verbose=False +) +print("Model initialized") + +def llm_engine(messages, stop_sequences=["Task", "<|endoftext|>"]) -> str: + output = "" + for chunk in model.create_chat_completion( + messages=messages, + max_tokens=2048, + temperature=0.0, + top_p=1.0, + top_k=50, + repeat_penalty=1.0, + stream=True + ): + content = chunk['choices'][0]['delta'].get('content') + if content: + if content in ["", "<|endoftext|>"]: + break + output += content + return output + +system_prompt = """You are an expert in composing functions. You are given a question and a set of possible functions. +Based on the question, you will need to make one or more function/tool calls to achieve the purpose. +If none of the functions can be used, point it out and refuse to answer. +If the given question lacks the parameters required by the function, also point it out. + +You have access to the following tools: +<> + +<> + +You can use imports in your code, but only from the following list of modules: <> + +The output MUST strictly adhere to the following format, and NO other text MUST be included. +The example format is as follows. Please make sure the parameter type is correct. If no function call is needed, please make the tool calls an empty list '[]'. +[ +{"name": "func_name1", "arguments": {"argument1": "value1", "argument2": "value2"}}, +... (more tool calls as required) +]""" + + +from agents import tool +import webbrowser + +@tool +def get_random_number_between(min: int, max: int) -> int: + """ + Gets a random number between min and max. + + Args: + min: The minimum number. + max: The maximum number. + + Returns: + A random number between min and max. + """ + return random.randint(min, max) + + +@tool +def get_weather(city: str) -> str: + """ + Returns the weather forecast for a given city. + + Args: + city: The name of the city. + + Returns: + A string with a mock weather forecast. + """ + url = 'https://wttr.in/{}?format=+%C,+%t'.format(city) + res = requests.get(url).text + + return f"The weather in {city} is {res.split(',')[0]} with a high of {res.split(',')[1][:-2]} degrees Celsius." + +@tool +def get_current_time() -> str: + """ + This is a tool that returns the current time. + It returns the current time as HH:MM. + """ + return f"The current time is {datetime.now().hour}:{datetime.now().minute}." + +@tool +def open_webbrowser(url: str) -> str: + """ + This is a tool that opens a web browser to the given website. + If the user asks to open a website or a browser, you should use this tool. + + Args: + url: The url to open. + """ + webbrowser.open(url) + return f"I opened {url.replace('https://', '').replace('www.', '')} in the browser." + + +agent = ReactJsonAgent(llm_engine = llm_engine, tools=[get_current_time, open_webbrowser, get_random_number_between, get_weather]) +print("Agent initialized!") +agent.run("What's the weather like in London?") \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 2350048..543af28 100644 --- a/poetry.lock +++ b/poetry.lock @@ -136,6 +136,17 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "diskcache" +version = "5.6.3" +description = "Disk Cache -- Disk and file backed persistent cache." +optional = false +python-versions = ">=3" +files = [ + {file = "diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19"}, + {file = "diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc"}, +] + [[package]] name = "exceptiongroup" version = "1.2.2" @@ -264,6 +275,45 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "llama-cpp-python" +version = "0.3.4" +description = "Python bindings for the llama.cpp library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "llama_cpp_python-0.3.4.tar.gz", hash = "sha256:52b37129c282753edf89213e852b8b46664b059116ae2213da9cb4a45374d23d"}, +] + +[package.dependencies] +diskcache = ">=5.6.1" +jinja2 = ">=2.11.3" +numpy = ">=1.20.0" +typing-extensions = ">=4.5.0" + +[package.extras] +all = ["llama_cpp_python[dev,server,test]"] +dev = ["black (>=23.3.0)", "httpx (>=0.24.1)", "mkdocs (>=1.4.3)", "mkdocs-material (>=9.1.18)", "mkdocstrings[python] (>=0.22.0)", "pytest (>=7.4.0)", "twine (>=4.0.2)"] +server = ["PyYAML (>=5.1)", "fastapi (>=0.100.0)", "pydantic-settings (>=2.0.1)", "sse-starlette (>=1.6.1)", "starlette-context (>=0.3.6,<0.4)", "uvicorn (>=0.22.0)"] +test = ["fastapi (>=0.100.0)", "httpx (>=0.24.1)", "huggingface-hub (>=0.23.0)", "pydantic-settings (>=2.0.1)", "pytest (>=7.4.0)", "scipy (>=1.10)", "sse-starlette (>=1.6.1)", "starlette-context (>=0.3.6,<0.4)"] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -288,6 +338,76 @@ profiling = ["gprof2dot"] rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] +[[package]] +name = "markupsafe" +version = "3.0.2" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +files = [ + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, +] + [[package]] name = "mdurl" version = "0.1.2" @@ -374,6 +494,184 @@ files = [ {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] +[[package]] +name = "pandas" +version = "2.2.3" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, + {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f"}, + {file = "pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"}, + {file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"}, + {file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"}, + {file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761"}, + {file = "pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e"}, + {file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.7" + +[package.extras] +all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] +aws = ["s3fs (>=2022.11.0)"] +clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] +compression = ["zstandard (>=0.19.0)"] +computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] +consortium-standard = ["dataframe-api-compat (>=0.1.7)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] +feather = ["pyarrow (>=10.0.1)"] +fss = ["fsspec (>=2022.11.0)"] +gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] +hdf5 = ["tables (>=3.8.0)"] +html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] +mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] +parquet = ["pyarrow (>=10.0.1)"] +performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] +plot = ["matplotlib (>=3.6.3)"] +postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +pyarrow = ["pyarrow (>=10.0.1)"] +spss = ["pyreadstat (>=1.2.0)"] +sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] +test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.9.2)"] + +[[package]] +name = "pillow" +version = "11.0.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pillow-11.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6619654954dc4936fcff82db8eb6401d3159ec6be81e33c6000dfd76ae189947"}, + {file = "pillow-11.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b3c5ac4bed7519088103d9450a1107f76308ecf91d6dabc8a33a2fcfb18d0fba"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a65149d8ada1055029fcb665452b2814fe7d7082fcb0c5bed6db851cb69b2086"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88a58d8ac0cc0e7f3a014509f0455248a76629ca9b604eca7dc5927cc593c5e9"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:c26845094b1af3c91852745ae78e3ea47abf3dbcd1cf962f16b9a5fbe3ee8488"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:1a61b54f87ab5786b8479f81c4b11f4d61702830354520837f8cc791ebba0f5f"}, + {file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:674629ff60030d144b7bca2b8330225a9b11c482ed408813924619c6f302fdbb"}, + {file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:598b4e238f13276e0008299bd2482003f48158e2b11826862b1eb2ad7c768b97"}, + {file = "pillow-11.0.0-cp310-cp310-win32.whl", hash = "sha256:9a0f748eaa434a41fccf8e1ee7a3eed68af1b690e75328fd7a60af123c193b50"}, + {file = "pillow-11.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:a5629742881bcbc1f42e840af185fd4d83a5edeb96475a575f4da50d6ede337c"}, + {file = "pillow-11.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:ee217c198f2e41f184f3869f3e485557296d505b5195c513b2bfe0062dc537f1"}, + {file = "pillow-11.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1c1d72714f429a521d8d2d018badc42414c3077eb187a59579f28e4270b4b0fc"}, + {file = "pillow-11.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:499c3a1b0d6fc8213519e193796eb1a86a1be4b1877d678b30f83fd979811d1a"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8b2351c85d855293a299038e1f89db92a2f35e8d2f783489c6f0b2b5f3fe8a3"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f4dba50cfa56f910241eb7f883c20f1e7b1d8f7d91c750cd0b318bad443f4d5"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5ddbfd761ee00c12ee1be86c9c0683ecf5bb14c9772ddbd782085779a63dd55b"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:45c566eb10b8967d71bf1ab8e4a525e5a93519e29ea071459ce517f6b903d7fa"}, + {file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b4fd7bd29610a83a8c9b564d457cf5bd92b4e11e79a4ee4716a63c959699b306"}, + {file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cb929ca942d0ec4fac404cbf520ee6cac37bf35be479b970c4ffadf2b6a1cad9"}, + {file = "pillow-11.0.0-cp311-cp311-win32.whl", hash = "sha256:006bcdd307cc47ba43e924099a038cbf9591062e6c50e570819743f5607404f5"}, + {file = "pillow-11.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:52a2d8323a465f84faaba5236567d212c3668f2ab53e1c74c15583cf507a0291"}, + {file = "pillow-11.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:16095692a253047fe3ec028e951fa4221a1f3ed3d80c397e83541a3037ff67c9"}, + {file = "pillow-11.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2c0a187a92a1cb5ef2c8ed5412dd8d4334272617f532d4ad4de31e0495bd923"}, + {file = "pillow-11.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:084a07ef0821cfe4858fe86652fffac8e187b6ae677e9906e192aafcc1b69903"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8069c5179902dcdce0be9bfc8235347fdbac249d23bd90514b7a47a72d9fecf4"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f02541ef64077f22bf4924f225c0fd1248c168f86e4b7abdedd87d6ebaceab0f"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:fcb4621042ac4b7865c179bb972ed0da0218a076dc1820ffc48b1d74c1e37fe9"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:00177a63030d612148e659b55ba99527803288cea7c75fb05766ab7981a8c1b7"}, + {file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8853a3bf12afddfdf15f57c4b02d7ded92c7a75a5d7331d19f4f9572a89c17e6"}, + {file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3107c66e43bda25359d5ef446f59c497de2b5ed4c7fdba0894f8d6cf3822dafc"}, + {file = "pillow-11.0.0-cp312-cp312-win32.whl", hash = "sha256:86510e3f5eca0ab87429dd77fafc04693195eec7fd6a137c389c3eeb4cfb77c6"}, + {file = "pillow-11.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:8ec4a89295cd6cd4d1058a5e6aec6bf51e0eaaf9714774e1bfac7cfc9051db47"}, + {file = "pillow-11.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:27a7860107500d813fcd203b4ea19b04babe79448268403172782754870dac25"}, + {file = "pillow-11.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcd1fb5bb7b07f64c15618c89efcc2cfa3e95f0e3bcdbaf4642509de1942a699"}, + {file = "pillow-11.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e038b0745997c7dcaae350d35859c9715c71e92ffb7e0f4a8e8a16732150f38"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ae08bd8ffc41aebf578c2af2f9d8749d91f448b3bfd41d7d9ff573d74f2a6b2"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d69bfd8ec3219ae71bcde1f942b728903cad25fafe3100ba2258b973bd2bc1b2"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:61b887f9ddba63ddf62fd02a3ba7add935d053b6dd7d58998c630e6dbade8527"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c6a660307ca9d4867caa8d9ca2c2658ab685de83792d1876274991adec7b93fa"}, + {file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:73e3a0200cdda995c7e43dd47436c1548f87a30bb27fb871f352a22ab8dcf45f"}, + {file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fba162b8872d30fea8c52b258a542c5dfd7b235fb5cb352240c8d63b414013eb"}, + {file = "pillow-11.0.0-cp313-cp313-win32.whl", hash = "sha256:f1b82c27e89fffc6da125d5eb0ca6e68017faf5efc078128cfaa42cf5cb38798"}, + {file = "pillow-11.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ba470552b48e5835f1d23ecb936bb7f71d206f9dfeee64245f30c3270b994de"}, + {file = "pillow-11.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:846e193e103b41e984ac921b335df59195356ce3f71dcfd155aa79c603873b84"}, + {file = "pillow-11.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4ad70c4214f67d7466bea6a08061eba35c01b1b89eaa098040a35272a8efb22b"}, + {file = "pillow-11.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ec0d5af64f2e3d64a165f490d96368bb5dea8b8f9ad04487f9ab60dc4bb6003"}, + {file = "pillow-11.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c809a70e43c7977c4a42aefd62f0131823ebf7dd73556fa5d5950f5b354087e2"}, + {file = "pillow-11.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4b60c9520f7207aaf2e1d94de026682fc227806c6e1f55bba7606d1c94dd623a"}, + {file = "pillow-11.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1e2688958a840c822279fda0086fec1fdab2f95bf2b717b66871c4ad9859d7e8"}, + {file = "pillow-11.0.0-cp313-cp313t-win32.whl", hash = "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8"}, + {file = "pillow-11.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904"}, + {file = "pillow-11.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3"}, + {file = "pillow-11.0.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2e46773dc9f35a1dd28bd6981332fd7f27bec001a918a72a79b4133cf5291dba"}, + {file = "pillow-11.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2679d2258b7f1192b378e2893a8a0a0ca472234d4c2c0e6bdd3380e8dfa21b6a"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda2616eb2313cbb3eebbe51f19362eb434b18e3bb599466a1ffa76a033fb916"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ec184af98a121fb2da42642dea8a29ec80fc3efbaefb86d8fdd2606619045d"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:8594f42df584e5b4bb9281799698403f7af489fba84c34d53d1c4bfb71b7c4e7"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:c12b5ae868897c7338519c03049a806af85b9b8c237b7d675b8c5e089e4a618e"}, + {file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:70fbbdacd1d271b77b7721fe3cdd2d537bbbd75d29e6300c672ec6bb38d9672f"}, + {file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5178952973e588b3f1360868847334e9e3bf49d19e169bbbdfaf8398002419ae"}, + {file = "pillow-11.0.0-cp39-cp39-win32.whl", hash = "sha256:8c676b587da5673d3c75bd67dd2a8cdfeb282ca38a30f37950511766b26858c4"}, + {file = "pillow-11.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:94f3e1780abb45062287b4614a5bc0874519c86a777d4a7ad34978e86428b8dd"}, + {file = "pillow-11.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:290f2cc809f9da7d6d622550bbf4c1e57518212da51b6a30fe8e0a270a5b78bd"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1187739620f2b365de756ce086fdb3604573337cc28a0d3ac4a01ab6b2d2a6d2"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fbbcb7b57dc9c794843e3d1258c0fbf0f48656d46ffe9e09b63bbd6e8cd5d0a2"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d203af30149ae339ad1b4f710d9844ed8796e97fda23ffbc4cc472968a47d0b"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a0d3b115009ebb8ac3d2ebec5c2982cc693da935f4ab7bb5c8ebe2f47d36f2"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:73853108f56df97baf2bb8b522f3578221e56f646ba345a372c78326710d3830"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e58876c91f97b0952eb766123bfef372792ab3f4e3e1f1a2267834c2ab131734"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:224aaa38177597bb179f3ec87eeefcce8e4f85e608025e9cfac60de237ba6316"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5bd2d3bdb846d757055910f0a59792d33b555800813c3b39ada1829c372ccb06"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:375b8dd15a1f5d2feafff536d47e22f69625c1aa92f12b339ec0b2ca40263273"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:daffdf51ee5db69a82dd127eabecce20729e21f7a3680cf7cbb23f0829189790"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7326a1787e3c7b0429659e0a944725e1b03eeaa10edd945a86dead1913383944"}, + {file = "pillow-11.0.0.tar.gz", hash = "sha256:72bacbaf24ac003fea9bff9837d1eedb6088758d41e100c1552930151f677739"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=8.1)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + [[package]] name = "pluggy" version = "1.5.0" @@ -425,6 +723,31 @@ tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytz" +version = "2024.2" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, + {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, +] + [[package]] name = "pyyaml" version = "6.0.2" @@ -762,6 +1085,17 @@ tensorflow = ["safetensors[numpy]", "tensorflow (>=2.11.0)"] testing = ["h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "hypothesis (>=6.70.2)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "safetensors[numpy]", "setuptools-rust (>=1.5.2)"] torch = ["safetensors[numpy]", "torch (>=1.10)"] +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + [[package]] name = "tokenizers" version = "0.21.0" @@ -936,6 +1270,17 @@ files = [ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] +[[package]] +name = "tzdata" +version = "2024.2" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, + {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, +] + [[package]] name = "urllib3" version = "2.2.3" @@ -956,4 +1301,4 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "985797a96ac1b58e4892479d96bbd1c696b452d760fa421a28b91ea8b3dbf977" +content-hash = "6c3841968936d66bf70e11c6c8e0a16fec6c2f4d88d79cd8ac5a412225e7cf56" diff --git a/pyproject.toml b/pyproject.toml index f3e6d91..7363250 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,6 +63,10 @@ transformers = ">=4.0.0" pytest = {version = ">=8.1.0", optional = true} requests = "^2.32.3" rich = "^13.9.4" +pandas = "^2.2.3" +jinja2 = "^3.1.4" +pillow = "^11.0.0" +llama-cpp-python = "^0.3.4" [build-system]