mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-10 00:28:31 +00:00
chore: refactor parser & constants, improve tools_handler, update tests
- Move parser constants to dedicated module with pre-compiled regex - Refactor CrewAgentParser to module functions; remove unused params - Improve tools_handler with instance attributes - Update tests to use module-level parser functions
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
from .cache.cache_handler import CacheHandler
|
from crewai.agents.cache.cache_handler import CacheHandler
|
||||||
from .parser import CrewAgentParser
|
from crewai.agents.parser import parse, AgentAction, AgentFinish, OutputParserException
|
||||||
from .tools_handler import ToolsHandler
|
from crewai.agents.tools_handler import ToolsHandler
|
||||||
|
|
||||||
__all__ = ["CacheHandler", "CrewAgentParser", "ToolsHandler"]
|
__all__ = ["CacheHandler", "parse", "AgentAction", "AgentFinish", "OutputParserException", "ToolsHandler"]
|
||||||
|
|||||||
27
src/crewai/agents/constants.py
Normal file
27
src/crewai/agents/constants.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
"""Constants for agent-related modules."""
|
||||||
|
|
||||||
|
import re
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
|
# crewai.agents.parser constants
|
||||||
|
|
||||||
|
FINAL_ANSWER_ACTION: Final[str] = "Final Answer:"
|
||||||
|
MISSING_ACTION_AFTER_THOUGHT_ERROR_MESSAGE: Final[str] = (
|
||||||
|
"I did it wrong. Invalid Format: I missed the 'Action:' after 'Thought:'. I will do right next, and don't use a tool I have already used.\n"
|
||||||
|
)
|
||||||
|
MISSING_ACTION_INPUT_AFTER_ACTION_ERROR_MESSAGE: Final[str] = (
|
||||||
|
"I did it wrong. Invalid Format: I missed the 'Action Input:' after 'Action:'. I will do right next, and don't use a tool I have already used.\n"
|
||||||
|
)
|
||||||
|
FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE: Final[str] = (
|
||||||
|
"I did it wrong. Tried to both perform Action and give a Final Answer at the same time, I must do one or the other"
|
||||||
|
)
|
||||||
|
UNABLE_TO_REPAIR_JSON_RESULTS: Final[list[str]] = ['""', "{}"]
|
||||||
|
ACTION_INPUT_REGEX: Final[re.Pattern[str]] = re.compile(
|
||||||
|
r"Action\s*\d*\s*:\s*(.*?)\s*Action\s*\d*\s*Input\s*\d*\s*:\s*(.*)", re.DOTALL
|
||||||
|
)
|
||||||
|
ACTION_REGEX: Final[re.Pattern[str]] = re.compile(
|
||||||
|
r"Action\s*\d*\s*:\s*(.*?)", re.DOTALL
|
||||||
|
)
|
||||||
|
ACTION_INPUT_ONLY_REGEX: Final[re.Pattern[str]] = re.compile(
|
||||||
|
r"\s*Action\s*\d*\s*Input\s*\d*\s*:\s*(.*)", re.DOTALL
|
||||||
|
)
|
||||||
@@ -1,50 +1,67 @@
|
|||||||
import re
|
"""Agent output parsing module for ReAct-style LLM responses.
|
||||||
from typing import Any, Optional, Union
|
|
||||||
|
This module provides parsing functionality for agent outputs that follow
|
||||||
|
the ReAct (Reasoning and Acting) format, converting them into structured
|
||||||
|
AgentAction or AgentFinish objects.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from json_repair import repair_json
|
from json_repair import repair_json
|
||||||
|
|
||||||
|
from crewai.agents.constants import (
|
||||||
|
ACTION_INPUT_REGEX,
|
||||||
|
ACTION_REGEX,
|
||||||
|
ACTION_INPUT_ONLY_REGEX,
|
||||||
|
FINAL_ANSWER_ACTION,
|
||||||
|
MISSING_ACTION_AFTER_THOUGHT_ERROR_MESSAGE,
|
||||||
|
MISSING_ACTION_INPUT_AFTER_ACTION_ERROR_MESSAGE,
|
||||||
|
UNABLE_TO_REPAIR_JSON_RESULTS,
|
||||||
|
)
|
||||||
from crewai.utilities import I18N
|
from crewai.utilities import I18N
|
||||||
|
|
||||||
FINAL_ANSWER_ACTION = "Final Answer:"
|
_I18N = I18N()
|
||||||
MISSING_ACTION_AFTER_THOUGHT_ERROR_MESSAGE = "I did it wrong. Invalid Format: I missed the 'Action:' after 'Thought:'. I will do right next, and don't use a tool I have already used.\n"
|
|
||||||
MISSING_ACTION_INPUT_AFTER_ACTION_ERROR_MESSAGE = "I did it wrong. Invalid Format: I missed the 'Action Input:' after 'Action:'. I will do right next, and don't use a tool I have already used.\n"
|
|
||||||
FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE = "I did it wrong. Tried to both perform Action and give a Final Answer at the same time, I must do one or the other"
|
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
class AgentAction:
|
class AgentAction:
|
||||||
|
"""Represents an action to be taken by an agent."""
|
||||||
|
|
||||||
thought: str
|
thought: str
|
||||||
tool: str
|
tool: str
|
||||||
tool_input: str
|
tool_input: str
|
||||||
text: str
|
text: str
|
||||||
result: str
|
result: str | None = None
|
||||||
|
|
||||||
def __init__(self, thought: str, tool: str, tool_input: str, text: str):
|
|
||||||
self.thought = thought
|
|
||||||
self.tool = tool
|
|
||||||
self.tool_input = tool_input
|
|
||||||
self.text = text
|
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
class AgentFinish:
|
class AgentFinish:
|
||||||
|
"""Represents the final answer from an agent."""
|
||||||
|
|
||||||
thought: str
|
thought: str
|
||||||
output: str
|
output: str
|
||||||
text: str
|
text: str
|
||||||
|
|
||||||
def __init__(self, thought: str, output: str, text: str):
|
|
||||||
self.thought = thought
|
|
||||||
self.output = output
|
|
||||||
self.text = text
|
|
||||||
|
|
||||||
|
|
||||||
class OutputParserException(Exception):
|
class OutputParserException(Exception):
|
||||||
error: str
|
"""Exception raised when output parsing fails.
|
||||||
|
|
||||||
def __init__(self, error: str):
|
Attributes:
|
||||||
|
error: The error message.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, error: str) -> None:
|
||||||
|
"""Initialize OutputParserException.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
error: The error message.
|
||||||
|
"""
|
||||||
self.error = error
|
self.error = error
|
||||||
|
super().__init__(error)
|
||||||
|
|
||||||
|
|
||||||
class CrewAgentParser:
|
def parse(text: str) -> AgentAction | AgentFinish:
|
||||||
"""Parses ReAct-style LLM calls that have a single tool input.
|
"""Parse agent output text into AgentAction or AgentFinish.
|
||||||
|
|
||||||
Expects output to be in one of two formats.
|
Expects output to be in one of two formats.
|
||||||
|
|
||||||
@@ -62,108 +79,117 @@ class CrewAgentParser:
|
|||||||
|
|
||||||
Thought: agent thought here
|
Thought: agent thought here
|
||||||
Final Answer: The temperature is 100 degrees
|
Final Answer: The temperature is 100 degrees
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: The agent output text to parse.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
AgentAction or AgentFinish based on the content.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
OutputParserException: If the text format is invalid.
|
||||||
"""
|
"""
|
||||||
|
thought = _extract_thought(text)
|
||||||
|
includes_answer = FINAL_ANSWER_ACTION in text
|
||||||
|
action_match = ACTION_INPUT_REGEX.search(text)
|
||||||
|
|
||||||
_i18n: I18N = I18N()
|
if includes_answer:
|
||||||
agent: Any = None
|
final_answer = text.split(FINAL_ANSWER_ACTION)[-1].strip()
|
||||||
|
# Check whether the final answer ends with triple backticks.
|
||||||
|
if final_answer.endswith("```"):
|
||||||
|
# Count occurrences of triple backticks in the final answer.
|
||||||
|
count = final_answer.count("```")
|
||||||
|
# If count is odd then it's an unmatched trailing set; remove it.
|
||||||
|
if count % 2 != 0:
|
||||||
|
final_answer = final_answer[:-3].rstrip()
|
||||||
|
return AgentFinish(thought=thought, output=final_answer, text=text)
|
||||||
|
|
||||||
def __init__(self, agent: Optional[Any] = None):
|
elif action_match:
|
||||||
self.agent = agent
|
action = action_match.group(1)
|
||||||
|
clean_action = _clean_action(action)
|
||||||
|
|
||||||
@staticmethod
|
action_input = action_match.group(2).strip()
|
||||||
def parse_text(text: str) -> Union[AgentAction, AgentFinish]:
|
|
||||||
"""
|
|
||||||
Static method to parse text into an AgentAction or AgentFinish without needing to instantiate the class.
|
|
||||||
|
|
||||||
Args:
|
tool_input = action_input.strip(" ").strip('"')
|
||||||
text: The text to parse.
|
safe_tool_input = _safe_repair_json(tool_input)
|
||||||
|
|
||||||
Returns:
|
return AgentAction(
|
||||||
Either an AgentAction or AgentFinish based on the parsed content.
|
thought=thought, tool=clean_action, tool_input=safe_tool_input, text=text
|
||||||
"""
|
|
||||||
parser = CrewAgentParser()
|
|
||||||
return parser.parse(text)
|
|
||||||
|
|
||||||
def parse(self, text: str) -> Union[AgentAction, AgentFinish]:
|
|
||||||
thought = self._extract_thought(text)
|
|
||||||
includes_answer = FINAL_ANSWER_ACTION in text
|
|
||||||
regex = (
|
|
||||||
r"Action\s*\d*\s*:[\s]*(.*?)[\s]*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"
|
|
||||||
)
|
)
|
||||||
action_match = re.search(regex, text, re.DOTALL)
|
|
||||||
if includes_answer:
|
|
||||||
final_answer = text.split(FINAL_ANSWER_ACTION)[-1].strip()
|
|
||||||
# Check whether the final answer ends with triple backticks.
|
|
||||||
if final_answer.endswith("```"):
|
|
||||||
# Count occurrences of triple backticks in the final answer.
|
|
||||||
count = final_answer.count("```")
|
|
||||||
# If count is odd then it's an unmatched trailing set; remove it.
|
|
||||||
if count % 2 != 0:
|
|
||||||
final_answer = final_answer[:-3].rstrip()
|
|
||||||
return AgentFinish(thought, final_answer, text)
|
|
||||||
|
|
||||||
elif action_match:
|
if not ACTION_REGEX.search(text):
|
||||||
action = action_match.group(1)
|
raise OutputParserException(
|
||||||
clean_action = self._clean_action(action)
|
f"{MISSING_ACTION_AFTER_THOUGHT_ERROR_MESSAGE}\n{_I18N.slice('final_answer_format')}",
|
||||||
|
)
|
||||||
|
elif not ACTION_INPUT_ONLY_REGEX.search(text):
|
||||||
|
raise OutputParserException(
|
||||||
|
MISSING_ACTION_INPUT_AFTER_ACTION_ERROR_MESSAGE,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
err_format = _I18N.slice("format_without_tools")
|
||||||
|
error = f"{err_format}"
|
||||||
|
raise OutputParserException(
|
||||||
|
error,
|
||||||
|
)
|
||||||
|
|
||||||
action_input = action_match.group(2).strip()
|
|
||||||
|
|
||||||
tool_input = action_input.strip(" ").strip('"')
|
def _extract_thought(text: str) -> str:
|
||||||
safe_tool_input = self._safe_repair_json(tool_input)
|
"""Extract the thought portion from the text.
|
||||||
|
|
||||||
return AgentAction(thought, clean_action, safe_tool_input, text)
|
Args:
|
||||||
|
text: The full agent output text.
|
||||||
|
|
||||||
if not re.search(r"Action\s*\d*\s*:[\s]*(.*?)", text, re.DOTALL):
|
Returns:
|
||||||
raise OutputParserException(
|
The extracted thought string.
|
||||||
f"{MISSING_ACTION_AFTER_THOUGHT_ERROR_MESSAGE}\n{self._i18n.slice('final_answer_format')}",
|
"""
|
||||||
)
|
thought_index = text.find("\nAction")
|
||||||
elif not re.search(
|
if thought_index == -1:
|
||||||
r"[\s]*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)", text, re.DOTALL
|
thought_index = text.find("\nFinal Answer")
|
||||||
):
|
if thought_index == -1:
|
||||||
raise OutputParserException(
|
return ""
|
||||||
MISSING_ACTION_INPUT_AFTER_ACTION_ERROR_MESSAGE,
|
thought = text[:thought_index].strip()
|
||||||
)
|
# Remove any triple backticks from the thought string
|
||||||
else:
|
thought = thought.replace("```", "").strip()
|
||||||
format = self._i18n.slice("format_without_tools")
|
return thought
|
||||||
error = f"{format}"
|
|
||||||
raise OutputParserException(
|
|
||||||
error,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _extract_thought(self, text: str) -> str:
|
|
||||||
thought_index = text.find("\nAction")
|
|
||||||
if thought_index == -1:
|
|
||||||
thought_index = text.find("\nFinal Answer")
|
|
||||||
if thought_index == -1:
|
|
||||||
return ""
|
|
||||||
thought = text[:thought_index].strip()
|
|
||||||
# Remove any triple backticks from the thought string
|
|
||||||
thought = thought.replace("```", "").strip()
|
|
||||||
return thought
|
|
||||||
|
|
||||||
def _clean_action(self, text: str) -> str:
|
def _clean_action(text: str) -> str:
|
||||||
"""Clean action string by removing non-essential formatting characters."""
|
"""Clean action string by removing non-essential formatting characters.
|
||||||
return text.strip().strip("*").strip()
|
|
||||||
|
|
||||||
def _safe_repair_json(self, tool_input: str) -> str:
|
Args:
|
||||||
UNABLE_TO_REPAIR_JSON_RESULTS = ['""', "{}"]
|
text: The action text to clean.
|
||||||
|
|
||||||
# Skip repair if the input starts and ends with square brackets
|
Returns:
|
||||||
# Explanation: The JSON parser has issues handling inputs that are enclosed in square brackets ('[]').
|
The cleaned action string.
|
||||||
# These are typically valid JSON arrays or strings that do not require repair. Attempting to repair such inputs
|
"""
|
||||||
# might lead to unintended alterations, such as wrapping the entire input in additional layers or modifying
|
return text.strip().strip("*").strip()
|
||||||
# the structure in a way that changes its meaning. By skipping the repair for inputs that start and end with
|
|
||||||
# square brackets, we preserve the integrity of these valid JSON structures and avoid unnecessary modifications.
|
|
||||||
if tool_input.startswith("[") and tool_input.endswith("]"):
|
|
||||||
return tool_input
|
|
||||||
|
|
||||||
# Before repair, handle common LLM issues:
|
|
||||||
# 1. Replace """ with " to avoid JSON parser errors
|
|
||||||
|
|
||||||
tool_input = tool_input.replace('"""', '"')
|
def _safe_repair_json(tool_input: str) -> str:
|
||||||
|
"""Safely repair JSON input.
|
||||||
|
|
||||||
result = repair_json(tool_input)
|
Args:
|
||||||
if result in UNABLE_TO_REPAIR_JSON_RESULTS:
|
tool_input: The tool input string to repair.
|
||||||
return tool_input
|
|
||||||
|
|
||||||
return str(result)
|
Returns:
|
||||||
|
The repaired JSON string or original if repair fails.
|
||||||
|
"""
|
||||||
|
# Skip repair if the input starts and ends with square brackets
|
||||||
|
# Explanation: The JSON parser has issues handling inputs that are enclosed in square brackets ('[]').
|
||||||
|
# These are typically valid JSON arrays or strings that do not require repair. Attempting to repair such inputs
|
||||||
|
# might lead to unintended alterations, such as wrapping the entire input in additional layers or modifying
|
||||||
|
# the structure in a way that changes its meaning. By skipping the repair for inputs that start and end with
|
||||||
|
# square brackets, we preserve the integrity of these valid JSON structures and avoid unnecessary modifications.
|
||||||
|
if tool_input.startswith("[") and tool_input.endswith("]"):
|
||||||
|
return tool_input
|
||||||
|
|
||||||
|
# Before repair, handle common LLM issues:
|
||||||
|
# 1. Replace """ with " to avoid JSON parser errors
|
||||||
|
|
||||||
|
tool_input = tool_input.replace('"""', '"')
|
||||||
|
|
||||||
|
result = repair_json(tool_input)
|
||||||
|
if result in UNABLE_TO_REPAIR_JSON_RESULTS:
|
||||||
|
return tool_input
|
||||||
|
|
||||||
|
return str(result)
|
||||||
|
|||||||
@@ -1,29 +1,41 @@
|
|||||||
from typing import Any, Optional, Union
|
"""Tools handler for managing tool execution and caching."""
|
||||||
|
|
||||||
from ..tools.cache_tools.cache_tools import CacheTools
|
from crewai.tools.cache_tools.cache_tools import CacheTools
|
||||||
from ..tools.tool_calling import InstructorToolCalling, ToolCalling
|
from crewai.tools.tool_calling import InstructorToolCalling, ToolCalling
|
||||||
from .cache.cache_handler import CacheHandler
|
from crewai.agents.cache.cache_handler import CacheHandler
|
||||||
|
|
||||||
|
|
||||||
class ToolsHandler:
|
class ToolsHandler:
|
||||||
"""Callback handler for tool usage."""
|
"""Callback handler for tool usage.
|
||||||
|
|
||||||
last_used_tool: Optional[ToolCalling] = None
|
Attributes:
|
||||||
cache: Optional[CacheHandler]
|
last_used_tool: The most recently used tool calling instance.
|
||||||
|
cache: Optional cache handler for storing tool outputs.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, cache: Optional[CacheHandler] = None):
|
def __init__(self, cache: CacheHandler | None = None) -> None:
|
||||||
"""Initialize the callback handler."""
|
"""Initialize the callback handler.
|
||||||
self.cache = cache
|
|
||||||
self.last_used_tool = None
|
Args:
|
||||||
|
cache: Optional cache handler for storing tool outputs.
|
||||||
|
"""
|
||||||
|
self.cache: CacheHandler | None = cache
|
||||||
|
self.last_used_tool: ToolCalling | InstructorToolCalling | None = None
|
||||||
|
|
||||||
def on_tool_use(
|
def on_tool_use(
|
||||||
self,
|
self,
|
||||||
calling: Union[ToolCalling, InstructorToolCalling],
|
calling: ToolCalling | InstructorToolCalling,
|
||||||
output: str,
|
output: str,
|
||||||
should_cache: bool = True,
|
should_cache: bool = True,
|
||||||
) -> Any:
|
) -> None:
|
||||||
"""Run when tool ends running."""
|
"""Run when tool ends running.
|
||||||
self.last_used_tool = calling # type: ignore # BUG?: Incompatible types in assignment (expression has type "Union[ToolCalling, InstructorToolCalling]", variable has type "ToolCalling")
|
|
||||||
|
Args:
|
||||||
|
calling: The tool calling instance.
|
||||||
|
output: The output from the tool execution.
|
||||||
|
should_cache: Whether to cache the tool output.
|
||||||
|
"""
|
||||||
|
self.last_used_tool = calling
|
||||||
if self.cache and should_cache and calling.tool_name != CacheTools().name:
|
if self.cache and should_cache and calling.tool_name != CacheTools().name:
|
||||||
self.cache.add(
|
self.cache.add(
|
||||||
tool=calling.tool_name,
|
tool=calling.tool_name,
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import json
|
|||||||
import re
|
import re
|
||||||
from typing import Any, Callable, Dict, List, Optional, Sequence, Union
|
from typing import Any, Callable, Dict, List, Optional, Sequence, Union
|
||||||
|
|
||||||
|
from crewai.agents.constants import FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE
|
||||||
from crewai.agents.parser import (
|
from crewai.agents.parser import (
|
||||||
FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE,
|
|
||||||
AgentAction,
|
AgentAction,
|
||||||
AgentFinish,
|
AgentFinish,
|
||||||
CrewAgentParser,
|
|
||||||
OutputParserException,
|
OutputParserException,
|
||||||
|
parse,
|
||||||
)
|
)
|
||||||
from crewai.llm import LLM
|
from crewai.llm import LLM
|
||||||
from crewai.llms.base_llm import BaseLLM
|
from crewai.llms.base_llm import BaseLLM
|
||||||
@@ -25,6 +25,7 @@ from crewai.cli.config import Settings
|
|||||||
|
|
||||||
console = Console()
|
console = Console()
|
||||||
|
|
||||||
|
|
||||||
def parse_tools(tools: List[BaseTool]) -> List[CrewStructuredTool]:
|
def parse_tools(tools: List[BaseTool]) -> List[CrewStructuredTool]:
|
||||||
"""Parse tools to be used for the task."""
|
"""Parse tools to be used for the task."""
|
||||||
tools_list = []
|
tools_list = []
|
||||||
@@ -122,7 +123,7 @@ def format_message_for_llm(prompt: str, role: str = "user") -> Dict[str, str]:
|
|||||||
def format_answer(answer: str) -> Union[AgentAction, AgentFinish]:
|
def format_answer(answer: str) -> Union[AgentAction, AgentFinish]:
|
||||||
"""Format a response from the LLM into an AgentAction or AgentFinish."""
|
"""Format a response from the LLM into an AgentAction or AgentFinish."""
|
||||||
try:
|
try:
|
||||||
return CrewAgentParser.parse_text(answer)
|
return parse(answer)
|
||||||
except Exception:
|
except Exception:
|
||||||
# If parsing fails, return a default AgentFinish
|
# If parsing fails, return a default AgentFinish
|
||||||
return AgentFinish(
|
return AgentFinish(
|
||||||
@@ -446,9 +447,16 @@ def show_agent_logs(
|
|||||||
def _print_current_organization():
|
def _print_current_organization():
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
if settings.org_uuid:
|
if settings.org_uuid:
|
||||||
console.print(f"Fetching agent from organization: {settings.org_name} ({settings.org_uuid})", style="bold blue")
|
console.print(
|
||||||
|
f"Fetching agent from organization: {settings.org_name} ({settings.org_uuid})",
|
||||||
|
style="bold blue",
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
console.print("No organization currently set. We recommend setting one before using: `crewai org switch <org_id>` command.", style="yellow")
|
console.print(
|
||||||
|
"No organization currently set. We recommend setting one before using: `crewai org switch <org_id>` command.",
|
||||||
|
style="yellow",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def load_agent_from_repository(from_repository: str) -> Dict[str, Any]:
|
def load_agent_from_repository(from_repository: str) -> Dict[str, Any]:
|
||||||
attributes: Dict[str, Any] = {}
|
attributes: Dict[str, Any] = {}
|
||||||
|
|||||||
@@ -5,17 +5,10 @@ from crewai.agents.crew_agent_executor import (
|
|||||||
AgentFinish,
|
AgentFinish,
|
||||||
OutputParserException,
|
OutputParserException,
|
||||||
)
|
)
|
||||||
from crewai.agents.parser import CrewAgentParser
|
from crewai.agents import parser
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
def test_valid_action_parsing_special_characters():
|
||||||
def parser():
|
|
||||||
agent = MockAgent()
|
|
||||||
p = CrewAgentParser(agent)
|
|
||||||
return p
|
|
||||||
|
|
||||||
|
|
||||||
def test_valid_action_parsing_special_characters(parser):
|
|
||||||
text = "Thought: Let's find the temperature\nAction: search\nAction Input: what's the temperature in SF?"
|
text = "Thought: Let's find the temperature\nAction: search\nAction Input: what's the temperature in SF?"
|
||||||
result = parser.parse(text)
|
result = parser.parse(text)
|
||||||
assert isinstance(result, AgentAction)
|
assert isinstance(result, AgentAction)
|
||||||
@@ -23,7 +16,7 @@ def test_valid_action_parsing_special_characters(parser):
|
|||||||
assert result.tool_input == "what's the temperature in SF?"
|
assert result.tool_input == "what's the temperature in SF?"
|
||||||
|
|
||||||
|
|
||||||
def test_valid_action_parsing_with_json_tool_input(parser):
|
def test_valid_action_parsing_with_json_tool_input():
|
||||||
text = """
|
text = """
|
||||||
Thought: Let's find the information
|
Thought: Let's find the information
|
||||||
Action: query
|
Action: query
|
||||||
@@ -36,7 +29,7 @@ def test_valid_action_parsing_with_json_tool_input(parser):
|
|||||||
assert result.tool_input == expected_tool_input
|
assert result.tool_input == expected_tool_input
|
||||||
|
|
||||||
|
|
||||||
def test_valid_action_parsing_with_quotes(parser):
|
def test_valid_action_parsing_with_quotes():
|
||||||
text = 'Thought: Let\'s find the temperature\nAction: search\nAction Input: "temperature in SF"'
|
text = 'Thought: Let\'s find the temperature\nAction: search\nAction Input: "temperature in SF"'
|
||||||
result = parser.parse(text)
|
result = parser.parse(text)
|
||||||
assert isinstance(result, AgentAction)
|
assert isinstance(result, AgentAction)
|
||||||
@@ -44,7 +37,7 @@ def test_valid_action_parsing_with_quotes(parser):
|
|||||||
assert result.tool_input == "temperature in SF"
|
assert result.tool_input == "temperature in SF"
|
||||||
|
|
||||||
|
|
||||||
def test_valid_action_parsing_with_curly_braces(parser):
|
def test_valid_action_parsing_with_curly_braces():
|
||||||
text = "Thought: Let's find the temperature\nAction: search\nAction Input: {temperature in SF}"
|
text = "Thought: Let's find the temperature\nAction: search\nAction Input: {temperature in SF}"
|
||||||
result = parser.parse(text)
|
result = parser.parse(text)
|
||||||
assert isinstance(result, AgentAction)
|
assert isinstance(result, AgentAction)
|
||||||
@@ -52,7 +45,7 @@ def test_valid_action_parsing_with_curly_braces(parser):
|
|||||||
assert result.tool_input == "{temperature in SF}"
|
assert result.tool_input == "{temperature in SF}"
|
||||||
|
|
||||||
|
|
||||||
def test_valid_action_parsing_with_angle_brackets(parser):
|
def test_valid_action_parsing_with_angle_brackets():
|
||||||
text = "Thought: Let's find the temperature\nAction: search\nAction Input: <temperature in SF>"
|
text = "Thought: Let's find the temperature\nAction: search\nAction Input: <temperature in SF>"
|
||||||
result = parser.parse(text)
|
result = parser.parse(text)
|
||||||
assert isinstance(result, AgentAction)
|
assert isinstance(result, AgentAction)
|
||||||
@@ -60,7 +53,7 @@ def test_valid_action_parsing_with_angle_brackets(parser):
|
|||||||
assert result.tool_input == "<temperature in SF>"
|
assert result.tool_input == "<temperature in SF>"
|
||||||
|
|
||||||
|
|
||||||
def test_valid_action_parsing_with_parentheses(parser):
|
def test_valid_action_parsing_with_parentheses():
|
||||||
text = "Thought: Let's find the temperature\nAction: search\nAction Input: (temperature in SF)"
|
text = "Thought: Let's find the temperature\nAction: search\nAction Input: (temperature in SF)"
|
||||||
result = parser.parse(text)
|
result = parser.parse(text)
|
||||||
assert isinstance(result, AgentAction)
|
assert isinstance(result, AgentAction)
|
||||||
@@ -68,7 +61,7 @@ def test_valid_action_parsing_with_parentheses(parser):
|
|||||||
assert result.tool_input == "(temperature in SF)"
|
assert result.tool_input == "(temperature in SF)"
|
||||||
|
|
||||||
|
|
||||||
def test_valid_action_parsing_with_mixed_brackets(parser):
|
def test_valid_action_parsing_with_mixed_brackets():
|
||||||
text = "Thought: Let's find the temperature\nAction: search\nAction Input: [temperature in {SF}]"
|
text = "Thought: Let's find the temperature\nAction: search\nAction Input: [temperature in {SF}]"
|
||||||
result = parser.parse(text)
|
result = parser.parse(text)
|
||||||
assert isinstance(result, AgentAction)
|
assert isinstance(result, AgentAction)
|
||||||
@@ -76,7 +69,7 @@ def test_valid_action_parsing_with_mixed_brackets(parser):
|
|||||||
assert result.tool_input == "[temperature in {SF}]"
|
assert result.tool_input == "[temperature in {SF}]"
|
||||||
|
|
||||||
|
|
||||||
def test_valid_action_parsing_with_nested_quotes(parser):
|
def test_valid_action_parsing_with_nested_quotes():
|
||||||
text = "Thought: Let's find the temperature\nAction: search\nAction Input: \"what's the temperature in 'SF'?\""
|
text = "Thought: Let's find the temperature\nAction: search\nAction Input: \"what's the temperature in 'SF'?\""
|
||||||
result = parser.parse(text)
|
result = parser.parse(text)
|
||||||
assert isinstance(result, AgentAction)
|
assert isinstance(result, AgentAction)
|
||||||
@@ -84,7 +77,7 @@ def test_valid_action_parsing_with_nested_quotes(parser):
|
|||||||
assert result.tool_input == "what's the temperature in 'SF'?"
|
assert result.tool_input == "what's the temperature in 'SF'?"
|
||||||
|
|
||||||
|
|
||||||
def test_valid_action_parsing_with_incomplete_json(parser):
|
def test_valid_action_parsing_with_incomplete_json():
|
||||||
text = 'Thought: Let\'s find the temperature\nAction: search\nAction Input: {"query": "temperature in SF"'
|
text = 'Thought: Let\'s find the temperature\nAction: search\nAction Input: {"query": "temperature in SF"'
|
||||||
result = parser.parse(text)
|
result = parser.parse(text)
|
||||||
assert isinstance(result, AgentAction)
|
assert isinstance(result, AgentAction)
|
||||||
@@ -92,7 +85,7 @@ def test_valid_action_parsing_with_incomplete_json(parser):
|
|||||||
assert result.tool_input == '{"query": "temperature in SF"}'
|
assert result.tool_input == '{"query": "temperature in SF"}'
|
||||||
|
|
||||||
|
|
||||||
def test_valid_action_parsing_with_special_characters(parser):
|
def test_valid_action_parsing_with_special_characters():
|
||||||
text = "Thought: Let's find the temperature\nAction: search\nAction Input: what is the temperature in SF? @$%^&*"
|
text = "Thought: Let's find the temperature\nAction: search\nAction Input: what is the temperature in SF? @$%^&*"
|
||||||
result = parser.parse(text)
|
result = parser.parse(text)
|
||||||
assert isinstance(result, AgentAction)
|
assert isinstance(result, AgentAction)
|
||||||
@@ -100,7 +93,7 @@ def test_valid_action_parsing_with_special_characters(parser):
|
|||||||
assert result.tool_input == "what is the temperature in SF? @$%^&*"
|
assert result.tool_input == "what is the temperature in SF? @$%^&*"
|
||||||
|
|
||||||
|
|
||||||
def test_valid_action_parsing_with_combination(parser):
|
def test_valid_action_parsing_with_combination():
|
||||||
text = 'Thought: Let\'s find the temperature\nAction: search\nAction Input: "[what is the temperature in SF?]"'
|
text = 'Thought: Let\'s find the temperature\nAction: search\nAction Input: "[what is the temperature in SF?]"'
|
||||||
result = parser.parse(text)
|
result = parser.parse(text)
|
||||||
assert isinstance(result, AgentAction)
|
assert isinstance(result, AgentAction)
|
||||||
@@ -108,7 +101,7 @@ def test_valid_action_parsing_with_combination(parser):
|
|||||||
assert result.tool_input == "[what is the temperature in SF?]"
|
assert result.tool_input == "[what is the temperature in SF?]"
|
||||||
|
|
||||||
|
|
||||||
def test_valid_action_parsing_with_mixed_quotes(parser):
|
def test_valid_action_parsing_with_mixed_quotes():
|
||||||
text = "Thought: Let's find the temperature\nAction: search\nAction Input: \"what's the temperature in SF?\""
|
text = "Thought: Let's find the temperature\nAction: search\nAction Input: \"what's the temperature in SF?\""
|
||||||
result = parser.parse(text)
|
result = parser.parse(text)
|
||||||
assert isinstance(result, AgentAction)
|
assert isinstance(result, AgentAction)
|
||||||
@@ -116,7 +109,7 @@ def test_valid_action_parsing_with_mixed_quotes(parser):
|
|||||||
assert result.tool_input == "what's the temperature in SF?"
|
assert result.tool_input == "what's the temperature in SF?"
|
||||||
|
|
||||||
|
|
||||||
def test_valid_action_parsing_with_newlines(parser):
|
def test_valid_action_parsing_with_newlines():
|
||||||
text = "Thought: Let's find the temperature\nAction: search\nAction Input: what is\nthe temperature in SF?"
|
text = "Thought: Let's find the temperature\nAction: search\nAction Input: what is\nthe temperature in SF?"
|
||||||
result = parser.parse(text)
|
result = parser.parse(text)
|
||||||
assert isinstance(result, AgentAction)
|
assert isinstance(result, AgentAction)
|
||||||
@@ -124,7 +117,7 @@ def test_valid_action_parsing_with_newlines(parser):
|
|||||||
assert result.tool_input == "what is\nthe temperature in SF?"
|
assert result.tool_input == "what is\nthe temperature in SF?"
|
||||||
|
|
||||||
|
|
||||||
def test_valid_action_parsing_with_escaped_characters(parser):
|
def test_valid_action_parsing_with_escaped_characters():
|
||||||
text = "Thought: Let's find the temperature\nAction: search\nAction Input: what is the temperature in SF? \\n"
|
text = "Thought: Let's find the temperature\nAction: search\nAction Input: what is the temperature in SF? \\n"
|
||||||
result = parser.parse(text)
|
result = parser.parse(text)
|
||||||
assert isinstance(result, AgentAction)
|
assert isinstance(result, AgentAction)
|
||||||
@@ -132,7 +125,7 @@ def test_valid_action_parsing_with_escaped_characters(parser):
|
|||||||
assert result.tool_input == "what is the temperature in SF? \\n"
|
assert result.tool_input == "what is the temperature in SF? \\n"
|
||||||
|
|
||||||
|
|
||||||
def test_valid_action_parsing_with_json_string(parser):
|
def test_valid_action_parsing_with_json_string():
|
||||||
text = 'Thought: Let\'s find the temperature\nAction: search\nAction Input: {"query": "temperature in SF"}'
|
text = 'Thought: Let\'s find the temperature\nAction: search\nAction Input: {"query": "temperature in SF"}'
|
||||||
result = parser.parse(text)
|
result = parser.parse(text)
|
||||||
assert isinstance(result, AgentAction)
|
assert isinstance(result, AgentAction)
|
||||||
@@ -140,7 +133,7 @@ def test_valid_action_parsing_with_json_string(parser):
|
|||||||
assert result.tool_input == '{"query": "temperature in SF"}'
|
assert result.tool_input == '{"query": "temperature in SF"}'
|
||||||
|
|
||||||
|
|
||||||
def test_valid_action_parsing_with_unbalanced_quotes(parser):
|
def test_valid_action_parsing_with_unbalanced_quotes():
|
||||||
text = "Thought: Let's find the temperature\nAction: search\nAction Input: \"what is the temperature in SF?"
|
text = "Thought: Let's find the temperature\nAction: search\nAction Input: \"what is the temperature in SF?"
|
||||||
result = parser.parse(text)
|
result = parser.parse(text)
|
||||||
assert isinstance(result, AgentAction)
|
assert isinstance(result, AgentAction)
|
||||||
@@ -148,61 +141,61 @@ def test_valid_action_parsing_with_unbalanced_quotes(parser):
|
|||||||
assert result.tool_input == "what is the temperature in SF?"
|
assert result.tool_input == "what is the temperature in SF?"
|
||||||
|
|
||||||
|
|
||||||
def test_clean_action_no_formatting(parser):
|
def test_clean_action_no_formatting():
|
||||||
action = "Ask question to senior researcher"
|
action = "Ask question to senior researcher"
|
||||||
cleaned_action = parser._clean_action(action)
|
cleaned_action = parser._clean_action(action)
|
||||||
assert cleaned_action == "Ask question to senior researcher"
|
assert cleaned_action == "Ask question to senior researcher"
|
||||||
|
|
||||||
|
|
||||||
def test_clean_action_with_leading_asterisks(parser):
|
def test_clean_action_with_leading_asterisks():
|
||||||
action = "** Ask question to senior researcher"
|
action = "** Ask question to senior researcher"
|
||||||
cleaned_action = parser._clean_action(action)
|
cleaned_action = parser._clean_action(action)
|
||||||
assert cleaned_action == "Ask question to senior researcher"
|
assert cleaned_action == "Ask question to senior researcher"
|
||||||
|
|
||||||
|
|
||||||
def test_clean_action_with_trailing_asterisks(parser):
|
def test_clean_action_with_trailing_asterisks():
|
||||||
action = "Ask question to senior researcher **"
|
action = "Ask question to senior researcher **"
|
||||||
cleaned_action = parser._clean_action(action)
|
cleaned_action = parser._clean_action(action)
|
||||||
assert cleaned_action == "Ask question to senior researcher"
|
assert cleaned_action == "Ask question to senior researcher"
|
||||||
|
|
||||||
|
|
||||||
def test_clean_action_with_leading_and_trailing_asterisks(parser):
|
def test_clean_action_with_leading_and_trailing_asterisks():
|
||||||
action = "** Ask question to senior researcher **"
|
action = "** Ask question to senior researcher **"
|
||||||
cleaned_action = parser._clean_action(action)
|
cleaned_action = parser._clean_action(action)
|
||||||
assert cleaned_action == "Ask question to senior researcher"
|
assert cleaned_action == "Ask question to senior researcher"
|
||||||
|
|
||||||
|
|
||||||
def test_clean_action_with_multiple_leading_asterisks(parser):
|
def test_clean_action_with_multiple_leading_asterisks():
|
||||||
action = "**** Ask question to senior researcher"
|
action = "**** Ask question to senior researcher"
|
||||||
cleaned_action = parser._clean_action(action)
|
cleaned_action = parser._clean_action(action)
|
||||||
assert cleaned_action == "Ask question to senior researcher"
|
assert cleaned_action == "Ask question to senior researcher"
|
||||||
|
|
||||||
|
|
||||||
def test_clean_action_with_multiple_trailing_asterisks(parser):
|
def test_clean_action_with_multiple_trailing_asterisks():
|
||||||
action = "Ask question to senior researcher ****"
|
action = "Ask question to senior researcher ****"
|
||||||
cleaned_action = parser._clean_action(action)
|
cleaned_action = parser._clean_action(action)
|
||||||
assert cleaned_action == "Ask question to senior researcher"
|
assert cleaned_action == "Ask question to senior researcher"
|
||||||
|
|
||||||
|
|
||||||
def test_clean_action_with_spaces_and_asterisks(parser):
|
def test_clean_action_with_spaces_and_asterisks():
|
||||||
action = " ** Ask question to senior researcher ** "
|
action = " ** Ask question to senior researcher ** "
|
||||||
cleaned_action = parser._clean_action(action)
|
cleaned_action = parser._clean_action(action)
|
||||||
assert cleaned_action == "Ask question to senior researcher"
|
assert cleaned_action == "Ask question to senior researcher"
|
||||||
|
|
||||||
|
|
||||||
def test_clean_action_with_only_asterisks(parser):
|
def test_clean_action_with_only_asterisks():
|
||||||
action = "****"
|
action = "****"
|
||||||
cleaned_action = parser._clean_action(action)
|
cleaned_action = parser._clean_action(action)
|
||||||
assert cleaned_action == ""
|
assert cleaned_action == ""
|
||||||
|
|
||||||
|
|
||||||
def test_clean_action_with_empty_string(parser):
|
def test_clean_action_with_empty_string():
|
||||||
action = ""
|
action = ""
|
||||||
cleaned_action = parser._clean_action(action)
|
cleaned_action = parser._clean_action(action)
|
||||||
assert cleaned_action == ""
|
assert cleaned_action == ""
|
||||||
|
|
||||||
|
|
||||||
def test_valid_final_answer_parsing(parser):
|
def test_valid_final_answer_parsing():
|
||||||
text = (
|
text = (
|
||||||
"Thought: I found the information\nFinal Answer: The temperature is 100 degrees"
|
"Thought: I found the information\nFinal Answer: The temperature is 100 degrees"
|
||||||
)
|
)
|
||||||
@@ -211,7 +204,7 @@ def test_valid_final_answer_parsing(parser):
|
|||||||
assert result.output == "The temperature is 100 degrees"
|
assert result.output == "The temperature is 100 degrees"
|
||||||
|
|
||||||
|
|
||||||
def test_missing_action_error(parser):
|
def test_missing_action_error():
|
||||||
text = "Thought: Let's find the temperature\nAction Input: what is the temperature in SF?"
|
text = "Thought: Let's find the temperature\nAction Input: what is the temperature in SF?"
|
||||||
with pytest.raises(OutputParserException) as exc_info:
|
with pytest.raises(OutputParserException) as exc_info:
|
||||||
parser.parse(text)
|
parser.parse(text)
|
||||||
@@ -220,27 +213,27 @@ def test_missing_action_error(parser):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_missing_action_input_error(parser):
|
def test_missing_action_input_error():
|
||||||
text = "Thought: Let's find the temperature\nAction: search"
|
text = "Thought: Let's find the temperature\nAction: search"
|
||||||
with pytest.raises(OutputParserException) as exc_info:
|
with pytest.raises(OutputParserException) as exc_info:
|
||||||
parser.parse(text)
|
parser.parse(text)
|
||||||
assert "I missed the 'Action Input:' after 'Action:'." in str(exc_info.value)
|
assert "I missed the 'Action Input:' after 'Action:'." in str(exc_info.value)
|
||||||
|
|
||||||
|
|
||||||
def test_safe_repair_json(parser):
|
def test_safe_repair_json():
|
||||||
invalid_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": Senior Researcher'
|
invalid_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": Senior Researcher'
|
||||||
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
||||||
result = parser._safe_repair_json(invalid_json)
|
result = parser._safe_repair_json(invalid_json)
|
||||||
assert result == expected_repaired_json
|
assert result == expected_repaired_json
|
||||||
|
|
||||||
|
|
||||||
def test_safe_repair_json_unrepairable(parser):
|
def test_safe_repair_json_unrepairable():
|
||||||
invalid_json = "{invalid_json"
|
invalid_json = "{invalid_json"
|
||||||
result = parser._safe_repair_json(invalid_json)
|
result = parser._safe_repair_json(invalid_json)
|
||||||
assert result == invalid_json # Should return the original if unrepairable
|
assert result == invalid_json # Should return the original if unrepairable
|
||||||
|
|
||||||
|
|
||||||
def test_safe_repair_json_missing_quotes(parser):
|
def test_safe_repair_json_missing_quotes():
|
||||||
invalid_json = (
|
invalid_json = (
|
||||||
'{task: "Research XAI", context: "Explainable AI", coworker: Senior Researcher}'
|
'{task: "Research XAI", context: "Explainable AI", coworker: Senior Researcher}'
|
||||||
)
|
)
|
||||||
@@ -249,77 +242,77 @@ def test_safe_repair_json_missing_quotes(parser):
|
|||||||
assert result == expected_repaired_json
|
assert result == expected_repaired_json
|
||||||
|
|
||||||
|
|
||||||
def test_safe_repair_json_unclosed_brackets(parser):
|
def test_safe_repair_json_unclosed_brackets():
|
||||||
invalid_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"'
|
invalid_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"'
|
||||||
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
||||||
result = parser._safe_repair_json(invalid_json)
|
result = parser._safe_repair_json(invalid_json)
|
||||||
assert result == expected_repaired_json
|
assert result == expected_repaired_json
|
||||||
|
|
||||||
|
|
||||||
def test_safe_repair_json_extra_commas(parser):
|
def test_safe_repair_json_extra_commas():
|
||||||
invalid_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher",}'
|
invalid_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher",}'
|
||||||
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
||||||
result = parser._safe_repair_json(invalid_json)
|
result = parser._safe_repair_json(invalid_json)
|
||||||
assert result == expected_repaired_json
|
assert result == expected_repaired_json
|
||||||
|
|
||||||
|
|
||||||
def test_safe_repair_json_trailing_commas(parser):
|
def test_safe_repair_json_trailing_commas():
|
||||||
invalid_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher",}'
|
invalid_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher",}'
|
||||||
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
||||||
result = parser._safe_repair_json(invalid_json)
|
result = parser._safe_repair_json(invalid_json)
|
||||||
assert result == expected_repaired_json
|
assert result == expected_repaired_json
|
||||||
|
|
||||||
|
|
||||||
def test_safe_repair_json_single_quotes(parser):
|
def test_safe_repair_json_single_quotes():
|
||||||
invalid_json = "{'task': 'Research XAI', 'context': 'Explainable AI', 'coworker': 'Senior Researcher'}"
|
invalid_json = "{'task': 'Research XAI', 'context': 'Explainable AI', 'coworker': 'Senior Researcher'}"
|
||||||
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
||||||
result = parser._safe_repair_json(invalid_json)
|
result = parser._safe_repair_json(invalid_json)
|
||||||
assert result == expected_repaired_json
|
assert result == expected_repaired_json
|
||||||
|
|
||||||
|
|
||||||
def test_safe_repair_json_mixed_quotes(parser):
|
def test_safe_repair_json_mixed_quotes():
|
||||||
invalid_json = "{'task': \"Research XAI\", 'context': \"Explainable AI\", 'coworker': 'Senior Researcher'}"
|
invalid_json = "{'task': \"Research XAI\", 'context': \"Explainable AI\", 'coworker': 'Senior Researcher'}"
|
||||||
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
||||||
result = parser._safe_repair_json(invalid_json)
|
result = parser._safe_repair_json(invalid_json)
|
||||||
assert result == expected_repaired_json
|
assert result == expected_repaired_json
|
||||||
|
|
||||||
|
|
||||||
def test_safe_repair_json_unescaped_characters(parser):
|
def test_safe_repair_json_unescaped_characters():
|
||||||
invalid_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher\n"}'
|
invalid_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher\n"}'
|
||||||
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
||||||
result = parser._safe_repair_json(invalid_json)
|
result = parser._safe_repair_json(invalid_json)
|
||||||
assert result == expected_repaired_json
|
assert result == expected_repaired_json
|
||||||
|
|
||||||
|
|
||||||
def test_safe_repair_json_missing_colon(parser):
|
def test_safe_repair_json_missing_colon():
|
||||||
invalid_json = '{"task" "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
invalid_json = '{"task" "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
||||||
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
||||||
result = parser._safe_repair_json(invalid_json)
|
result = parser._safe_repair_json(invalid_json)
|
||||||
assert result == expected_repaired_json
|
assert result == expected_repaired_json
|
||||||
|
|
||||||
|
|
||||||
def test_safe_repair_json_missing_comma(parser):
|
def test_safe_repair_json_missing_comma():
|
||||||
invalid_json = '{"task": "Research XAI" "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
invalid_json = '{"task": "Research XAI" "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
||||||
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
||||||
result = parser._safe_repair_json(invalid_json)
|
result = parser._safe_repair_json(invalid_json)
|
||||||
assert result == expected_repaired_json
|
assert result == expected_repaired_json
|
||||||
|
|
||||||
|
|
||||||
def test_safe_repair_json_unexpected_trailing_characters(parser):
|
def test_safe_repair_json_unexpected_trailing_characters():
|
||||||
invalid_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"} random text'
|
invalid_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"} random text'
|
||||||
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
|
||||||
result = parser._safe_repair_json(invalid_json)
|
result = parser._safe_repair_json(invalid_json)
|
||||||
assert result == expected_repaired_json
|
assert result == expected_repaired_json
|
||||||
|
|
||||||
|
|
||||||
def test_safe_repair_json_special_characters_key(parser):
|
def test_safe_repair_json_special_characters_key():
|
||||||
invalid_json = '{"task!@#": "Research XAI", "context$%^": "Explainable AI", "coworker&*()": "Senior Researcher"}'
|
invalid_json = '{"task!@#": "Research XAI", "context$%^": "Explainable AI", "coworker&*()": "Senior Researcher"}'
|
||||||
expected_repaired_json = '{"task!@#": "Research XAI", "context$%^": "Explainable AI", "coworker&*()": "Senior Researcher"}'
|
expected_repaired_json = '{"task!@#": "Research XAI", "context$%^": "Explainable AI", "coworker&*()": "Senior Researcher"}'
|
||||||
result = parser._safe_repair_json(invalid_json)
|
result = parser._safe_repair_json(invalid_json)
|
||||||
assert result == expected_repaired_json
|
assert result == expected_repaired_json
|
||||||
|
|
||||||
|
|
||||||
def test_parsing_with_whitespace(parser):
|
def test_parsing_with_whitespace():
|
||||||
text = " Thought: Let's find the temperature \n Action: search \n Action Input: what is the temperature in SF? "
|
text = " Thought: Let's find the temperature \n Action: search \n Action Input: what is the temperature in SF? "
|
||||||
result = parser.parse(text)
|
result = parser.parse(text)
|
||||||
assert isinstance(result, AgentAction)
|
assert isinstance(result, AgentAction)
|
||||||
@@ -327,7 +320,7 @@ def test_parsing_with_whitespace(parser):
|
|||||||
assert result.tool_input == "what is the temperature in SF?"
|
assert result.tool_input == "what is the temperature in SF?"
|
||||||
|
|
||||||
|
|
||||||
def test_parsing_with_special_characters(parser):
|
def test_parsing_with_special_characters():
|
||||||
text = 'Thought: Let\'s find the temperature\nAction: search\nAction Input: "what is the temperature in SF?"'
|
text = 'Thought: Let\'s find the temperature\nAction: search\nAction Input: "what is the temperature in SF?"'
|
||||||
result = parser.parse(text)
|
result = parser.parse(text)
|
||||||
assert isinstance(result, AgentAction)
|
assert isinstance(result, AgentAction)
|
||||||
@@ -335,7 +328,7 @@ def test_parsing_with_special_characters(parser):
|
|||||||
assert result.tool_input == "what is the temperature in SF?"
|
assert result.tool_input == "what is the temperature in SF?"
|
||||||
|
|
||||||
|
|
||||||
def test_integration_valid_and_invalid(parser):
|
def test_integration_valid_and_invalid():
|
||||||
text = """
|
text = """
|
||||||
Thought: Let's find the temperature
|
Thought: Let's find the temperature
|
||||||
Action: search
|
Action: search
|
||||||
@@ -365,9 +358,4 @@ def test_integration_valid_and_invalid(parser):
|
|||||||
assert isinstance(results[3], OutputParserException)
|
assert isinstance(results[3], OutputParserException)
|
||||||
|
|
||||||
|
|
||||||
class MockAgent:
|
|
||||||
def increment_formatting_errors(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: ADD TEST TO MAKE SURE ** REMOVAL DOESN'T MESS UP ANYTHING
|
# TODO: ADD TEST TO MAKE SURE ** REMOVAL DOESN'T MESS UP ANYTHING
|
||||||
|
|||||||
Reference in New Issue
Block a user