Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions api/core/agent/output_parser/cot_output_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ def parse_action(action) -> str | AgentScratchpadUnit.Action:
action = action[0]

for key, value in action.items():
if "input" in key.lower():
key_lower = key.lower()
if "input" in key_lower:
action_input = value
else:
elif key_lower == "action":
action_name = value

if action_name is not None and action_input is not None:
Expand All @@ -40,13 +41,17 @@ def parse_action(action) -> str | AgentScratchpadUnit.Action:
return json.dumps(action)

def extra_json_from_code_block(code_block) -> list[list | dict]:
blocks = re.findall(r"```[json]*\s*([\[{].*[]}])\s*```", code_block, re.DOTALL | re.IGNORECASE)
# One match per fenced block: a non-greedy body keeps two separate
# fences from being read as one object spanning both (#45324).
blocks = re.findall(r"```(?:json)?\s*(.*?)\s*```", code_block, re.DOTALL | re.IGNORECASE)
if not blocks:
return []
try:
json_blocks = []
for block in blocks:
json_text = re.sub(r"^[a-zA-Z]+\n", "", block.strip(), flags=re.MULTILINE)
if not json_text.lstrip().startswith(("{", "[")):
continue
json_blocks.append(json.loads(json_text, strict=False))
return json_blocks
except Exception:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,38 @@ def test_json_list_unwrap(self, make_chunk, usage_dict) -> None:
AgentScratchpadUnit.Action(action_name="lookup", action_input="abc")
]

def test_action_name_ignores_supplementary_keys(self, make_chunk, usage_dict) -> None:
content = (
'{"action": "google_search", "action_input": {"query": "latest AI news"}, '
'"thought": "I should search Google for the latest AI news"}'
)
chunks = [make_chunk(content)]
result = list(CotAgentOutputParser.handle_react_stream_output(chunks, usage_dict))
assert result == [
AgentScratchpadUnit.Action(
action_name="google_search",
action_input={"query": "latest AI news"},
)
]

def test_code_block_multiple_fences_are_parsed_independently(self, make_chunk, usage_dict) -> None:
content = """```json
{"action": "a1", "input": "x"}
```

Some prose between blocks.

```json
{"action": "a2", "input": "y"}
```"""
chunks = [make_chunk(content)]
result = list(CotAgentOutputParser.handle_react_stream_output(chunks, usage_dict))
actions = [item for item in result if isinstance(item, AgentScratchpadUnit.Action)]
assert actions == [
AgentScratchpadUnit.Action(action_name="a1", action_input="x"),
AgentScratchpadUnit.Action(action_name="a2", action_input="y"),
]

def test_json_missing_fields_returns_string(self, make_chunk, usage_dict) -> None:
content = '{"foo": "bar"}'
chunks = [make_chunk(content)]
Expand Down