Files
wehub-resource-sync 1443d3fdf9
Ruff Format Check / Ruff Format & Lint (push) Failing after 7m39s
Deploy VitePress site to Pages / build (push) Failing after 9m11s
Deploy VitePress site to Pages / Deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:26 +08:00

187 lines
5.7 KiB
Python

from __future__ import annotations
import io
from types import SimpleNamespace
import pytest
from rich.console import Console
from yuxi_cli.agent_eval import AgentEvalError, AgentEvalOptions, extract_query, run_langfuse_agent_experiment
from yuxi_cli.config import ConfigStore, Remote
class FakeResult:
def __init__(self, outputs: list[str]):
self.item_results = outputs
self.output = outputs[0] if outputs else ""
@classmethod
def single(cls, output: str):
return cls([output])
def format(self, *, include_item_results: bool) -> str:
assert include_item_results is True
return f"formatted: {self.output}"
class FakeDataset:
def __init__(self, item, *, result: FakeResult | None = None):
self.item = item
self.items = [item]
self.result = result
self.run_kwargs = None
def run_experiment(self, **kwargs):
self.run_kwargs = kwargs
output = kwargs["task"](item=self.item)
return self.result or FakeResult.single(output)
class FakePartialDataset:
def __init__(self):
self.items = [
SimpleNamespace(id="item-1", input="hello"),
SimpleNamespace(id="item-2", input="world"),
]
self.run_kwargs = None
def run_experiment(self, **kwargs):
self.run_kwargs = kwargs
kwargs["task"](item=self.items[0])
return FakeResult.single("final answer")
class FakeLangfuse:
def __init__(self, dataset):
self.dataset = dataset
self.flushed = 0
def get_dataset(self, name: str):
assert name == "agent-eval-smoke"
return self.dataset
def flush(self):
self.flushed += 1
class FakeYuxiClient:
calls = []
def __init__(self, remote: Remote, timeout: float = 30.0):
self.remote = remote
self.timeout = timeout
def __enter__(self):
return self
def __exit__(self, *_exc):
return None
def run_agent_eval(self, **kwargs):
self.calls.append({"remote": self.remote, "client_timeout": self.timeout, "kwargs": kwargs})
return {"status": "completed", "output": "final answer"}
def _console():
return Console(file=io.StringIO(), force_terminal=False)
def test_extract_query_supports_string_and_common_fields():
assert extract_query("hello") == "hello"
assert extract_query({"query": "hello"}) == "hello"
assert extract_query({"question": "hello"}) == "hello"
assert extract_query({"prompt": "hello"}) == "hello"
def test_extract_query_rejects_unrecognized_input():
with pytest.raises(AgentEvalError, match="无法从 Langfuse dataset item input 中提取 query"):
extract_query({"text": "hello"})
def test_run_langfuse_agent_experiment_uses_remote_api_key(tmp_path):
store = ConfigStore(tmp_path / "config.toml")
config = store.load()
remote = config.get_remote("local")
remote.api_key = "yxkey_local"
store.save(config)
dataset = FakeDataset(SimpleNamespace(id="item-1", input={"query": "2+2=?"}))
langfuse = FakeLangfuse(dataset)
console = _console()
FakeYuxiClient.calls = []
run_langfuse_agent_experiment(
store,
None,
AgentEvalOptions(
dataset_name="agent-eval-smoke",
agent_slug="default-chatbot",
experiment_name="exp-1",
max_concurrency=2,
timeout_seconds=123,
),
console,
langfuse_factory=lambda: langfuse,
client_factory=FakeYuxiClient,
)
assert dataset.run_kwargs["name"] == "exp-1"
assert dataset.run_kwargs["max_concurrency"] == 2
assert dataset.run_kwargs["metadata"] == {
"source": "agent_evaluation",
"agent_slug": "default-chatbot",
"dataset_name": "agent-eval-smoke",
"remote": "local",
}
call = FakeYuxiClient.calls[0]
assert call["remote"].name == "local"
assert call["client_timeout"] == 123
assert call["kwargs"]["query"] == "2+2=?"
assert call["kwargs"]["agent_slug"] == "default-chatbot"
assert "api_key" not in call["kwargs"]
assert call["kwargs"]["timeout_seconds"] == 123
assert call["kwargs"]["evaluation"] == {
"dataset_name": "agent-eval-smoke",
"dataset_item_id": "item-1",
"experiment_name": "exp-1",
}
assert call["kwargs"]["meta"]["request_id"].startswith("eval-")
assert "formatted: final answer" in console.file.getvalue()
assert langfuse.flushed == 1
def test_run_langfuse_agent_experiment_requires_login(tmp_path):
store = ConfigStore(tmp_path / "config.toml")
with pytest.raises(AgentEvalError, match="remote 尚未登录"):
run_langfuse_agent_experiment(
store,
None,
AgentEvalOptions(dataset_name="agent-eval-smoke", agent_slug="default-chatbot"),
_console(),
langfuse_factory=lambda: FakeLangfuse(FakeDataset(SimpleNamespace(id="1", input="hello"))),
client_factory=FakeYuxiClient,
)
def test_run_langfuse_agent_experiment_rejects_partial_langfuse_results(tmp_path):
store = ConfigStore(tmp_path / "config.toml")
config = store.load()
config.get_remote("local").api_key = "yxkey_local"
store.save(config)
langfuse = FakeLangfuse(FakePartialDataset())
FakeYuxiClient.calls = []
with pytest.raises(AgentEvalError, match="1/2 个 item 成功写入"):
run_langfuse_agent_experiment(
store,
None,
AgentEvalOptions(
dataset_name="agent-eval-smoke",
agent_slug="default-chatbot",
experiment_name="exp-1",
),
_console(),
langfuse_factory=lambda: langfuse,
client_factory=FakeYuxiClient,
)