mirror of
https://github.com/langgenius/dify-plugin-sdks.git
synced 2026-07-22 18:35:29 -04:00
ef530695c7
* feat(python): Enable Ruff ruleset (RUF) Refactor the codebase to address and resolve pre-existing style violations flagged by the Ruff ruleset. * feat(python): disable preview mode in Ruff Preview rules in Ruff are unstable and subject to frequent changes. Disabling them helps prevent potential confusion for developers. * chore(python): disable deprecated UP038 rule in Ruff The UP038 rule is deprecated and its suggested practice is not recommended. Disabling this rule helps avoid unnecessary code rewrites.
213 lines
8.0 KiB
Python
213 lines
8.0 KiB
Python
import json
|
|
import re
|
|
from collections.abc import Generator
|
|
from typing import Union
|
|
|
|
from dify_plugin.entities.model.llm import LLMResultChunk
|
|
from dify_plugin.interfaces.agent import AgentScratchpadUnit
|
|
|
|
|
|
class CotAgentOutputParser:
|
|
@classmethod
|
|
def handle_react_stream_output(
|
|
cls, llm_response: Generator[LLMResultChunk, None, None], usage_dict: dict
|
|
) -> Generator[Union[str, AgentScratchpadUnit.Action], None, None]:
|
|
def parse_action(json_str):
|
|
try:
|
|
action = json.loads(json_str, strict=False)
|
|
action_name = None
|
|
action_input = None
|
|
|
|
# cohere always returns a list
|
|
if isinstance(action, list) and len(action) == 1:
|
|
action = action[0]
|
|
|
|
for key, value in action.items():
|
|
if "input" in key.lower():
|
|
action_input = value
|
|
else:
|
|
action_name = value
|
|
|
|
if action_name is not None and action_input is not None:
|
|
return AgentScratchpadUnit.Action(
|
|
action_name=action_name,
|
|
action_input=action_input,
|
|
)
|
|
else:
|
|
return json_str or ""
|
|
except Exception:
|
|
return json_str or ""
|
|
|
|
def extra_json_from_code_block(
|
|
code_block,
|
|
) -> Generator[Union[str, AgentScratchpadUnit.Action], None, None]:
|
|
code_blocks = re.findall(r"```(.*?)```", code_block, re.DOTALL)
|
|
if not code_blocks:
|
|
return
|
|
for block in code_blocks:
|
|
json_text = re.sub(
|
|
r"^[a-zA-Z]+\n", "", block.strip(), flags=re.MULTILINE
|
|
)
|
|
yield parse_action(json_text)
|
|
|
|
code_block_cache = ""
|
|
code_block_delimiter_count = 0
|
|
in_code_block = False
|
|
json_cache = ""
|
|
json_quote_count = 0
|
|
in_json = False
|
|
got_json = False
|
|
|
|
action_cache = ""
|
|
action_str = "action:"
|
|
action_idx = 0
|
|
|
|
thought_cache = ""
|
|
thought_str = "thought:"
|
|
thought_idx = 0
|
|
|
|
last_character = ""
|
|
|
|
for response in llm_response:
|
|
if response.delta.usage:
|
|
usage_dict["usage"] = response.delta.usage
|
|
response_content = response.delta.message.content
|
|
if not isinstance(response_content, str):
|
|
continue
|
|
|
|
# stream
|
|
index = 0
|
|
while index < len(response_content):
|
|
steps = 1
|
|
delta = response_content[index : index + steps]
|
|
yield_delta = False
|
|
|
|
if delta == "`":
|
|
last_character = delta
|
|
code_block_cache += delta
|
|
code_block_delimiter_count += 1
|
|
else:
|
|
if not in_code_block:
|
|
if code_block_delimiter_count > 0:
|
|
last_character = delta
|
|
yield code_block_cache
|
|
code_block_cache = ""
|
|
else:
|
|
last_character = delta
|
|
code_block_cache += delta
|
|
code_block_delimiter_count = 0
|
|
|
|
if not in_code_block and not in_json:
|
|
if delta.lower() == action_str[action_idx] and action_idx == 0:
|
|
if last_character not in {"\n", " ", ""}:
|
|
yield_delta = True
|
|
else:
|
|
last_character = delta
|
|
action_cache += delta
|
|
action_idx += 1
|
|
if action_idx == len(action_str):
|
|
action_cache = ""
|
|
action_idx = 0
|
|
index += steps
|
|
continue
|
|
elif delta.lower() == action_str[action_idx] and action_idx > 0:
|
|
last_character = delta
|
|
action_cache += delta
|
|
action_idx += 1
|
|
if action_idx == len(action_str):
|
|
action_cache = ""
|
|
action_idx = 0
|
|
index += steps
|
|
continue
|
|
else:
|
|
if action_cache:
|
|
last_character = delta
|
|
yield action_cache
|
|
action_cache = ""
|
|
action_idx = 0
|
|
|
|
if delta.lower() == thought_str[thought_idx] and thought_idx == 0:
|
|
if last_character not in {"\n", " ", ""}:
|
|
yield_delta = True
|
|
else:
|
|
last_character = delta
|
|
thought_cache += delta
|
|
thought_idx += 1
|
|
if thought_idx == len(thought_str):
|
|
thought_cache = ""
|
|
thought_idx = 0
|
|
index += steps
|
|
continue
|
|
elif delta.lower() == thought_str[thought_idx] and thought_idx > 0:
|
|
last_character = delta
|
|
thought_cache += delta
|
|
thought_idx += 1
|
|
if thought_idx == len(thought_str):
|
|
thought_cache = ""
|
|
thought_idx = 0
|
|
index += steps
|
|
continue
|
|
else:
|
|
if thought_cache:
|
|
last_character = delta
|
|
yield thought_cache
|
|
thought_cache = ""
|
|
thought_idx = 0
|
|
|
|
if yield_delta:
|
|
index += steps
|
|
last_character = delta
|
|
yield delta
|
|
continue
|
|
|
|
if code_block_delimiter_count == 3:
|
|
if in_code_block:
|
|
last_character = delta
|
|
yield from extra_json_from_code_block(code_block_cache)
|
|
code_block_cache = ""
|
|
|
|
in_code_block = not in_code_block
|
|
code_block_delimiter_count = 0
|
|
|
|
if not in_code_block:
|
|
# handle single json
|
|
if delta == "{":
|
|
json_quote_count += 1
|
|
in_json = True
|
|
last_character = delta
|
|
json_cache += delta
|
|
elif delta == "}":
|
|
last_character = delta
|
|
json_cache += delta
|
|
if json_quote_count > 0:
|
|
json_quote_count -= 1
|
|
if json_quote_count == 0:
|
|
in_json = False
|
|
got_json = True
|
|
index += steps
|
|
continue
|
|
else:
|
|
if in_json:
|
|
last_character = delta
|
|
json_cache += delta
|
|
|
|
if got_json:
|
|
got_json = False
|
|
last_character = delta
|
|
yield parse_action(json_cache)
|
|
json_cache = ""
|
|
json_quote_count = 0
|
|
in_json = False
|
|
|
|
if not in_code_block and not in_json:
|
|
last_character = delta
|
|
yield delta.replace("`", "")
|
|
|
|
index += steps
|
|
|
|
if code_block_cache:
|
|
yield code_block_cache
|
|
|
|
if json_cache:
|
|
yield parse_action(json_cache)
|