项目文件夹

文件
wehub-resource-sync 97e91a83f3
Ruff / Ruff (push) Has been cancelled
Test / Core Tests (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.10) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.11) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.12) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.13) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.9) (push) Has been cancelled
Test / Full Coverage (Python 3.11) (push) Has been cancelled
Test / Core Provider Tests (OpenAI) (push) Has been cancelled
Test / Core Provider Tests (Anthropic) (push) Has been cancelled
Test / Core Provider Tests (Google) (push) Has been cancelled
Test / Core Provider Tests (Other) (push) Has been cancelled
Test / Anthropic Tests (push) Has been cancelled
Test / Gemini Tests (push) Has been cancelled
Test / Google GenAI Tests (push) Has been cancelled
Test / Vertex AI Tests (push) Has been cancelled
Test / OpenAI Tests (push) Has been cancelled
Test / Writer Tests (push) Has been cancelled
Test / Auto Client Tests (push) Has been cancelled
ty / type-check (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:36:38 +08:00

149 行
4.6 KiB
Python

from __future__ import annotations
from types import SimpleNamespace
from typing import Any
import pytest
from pydantic import BaseModel
from instructor import Mode
from instructor.v2.providers.genai.handlers import (
GenAIToolsHandler,
GenAIStructuredOutputsHandler,
reask_genai_structured_outputs,
reask_genai_tools,
)
from tests.v2._fake_genai import FakeContent, FakePart, install_fake_genai
class FakeModelContent(FakeContent):
def __init__(self, parts: list[FakePart], role: str = "model") -> None:
super().__init__(role=role, parts=parts)
class FakeGenerateContentConfig:
def __init__(self, **kwargs: Any) -> None:
self.kwargs = kwargs
def _install_fake_genai_types(monkeypatch: pytest.MonkeyPatch) -> None:
install_fake_genai(
monkeypatch,
extra_types={
"ModelContent": FakeModelContent,
"GenerateContentConfig": FakeGenerateContentConfig,
},
)
def test_reask_genai_tools_without_function_call_appends_user_error(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_install_fake_genai_types(monkeypatch)
existing_content = FakeContent(role="model", parts=[FakePart(text="hi")])
response = SimpleNamespace(candidates=[SimpleNamespace(content=existing_content)])
result = reask_genai_tools({"contents": []}, response, ValueError("bad schema"))
assert result["contents"][0] is existing_content
assert result["contents"][1].role == "user"
assert "Validation Error found" in result["contents"][1].parts[0].text
def test_reask_genai_tools_with_function_call_appends_tool_response(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_install_fake_genai_types(monkeypatch)
function_call = SimpleNamespace(name="Answer")
content = FakeContent(role="model", parts=[FakePart(function_call=function_call)])
response = SimpleNamespace(candidates=[SimpleNamespace(content=content)])
result = reask_genai_tools({"contents": []}, response, ValueError("bad schema"))
assert result["contents"][0] is content
assert result["contents"][1].role == "tool"
function_response = result["contents"][1].parts[0].function_response
assert function_response["name"] == "Answer"
assert "Validation Error found" in function_response["response"]["error"]
def test_reask_genai_structured_outputs_appends_model_content(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_install_fake_genai_types(monkeypatch)
kwargs = {"contents": []}
response = SimpleNamespace(text='{"bad": true}')
result = reask_genai_structured_outputs(kwargs, response, ValueError("bad json"))
assert isinstance(result["contents"][-1], FakeModelContent)
assert "bad json" in result["contents"][-1].parts[0].text
assert '{"bad": true}' in result["contents"][-1].parts[0].text
def test_tools_handler_prepare_request_without_response_model(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_install_fake_genai_types(monkeypatch)
monkeypatch.setattr(
"instructor.v2.providers.gemini.utils.convert_to_genai_messages",
lambda messages: ["converted", *messages],
)
monkeypatch.setattr(
"instructor.v2.providers.genai.handlers.extract_genai_multimodal_content",
lambda contents, autodetect_images: [*contents, autodetect_images],
)
handler = GenAIToolsHandler(mode=Mode.TOOLS)
model, kwargs = handler.prepare_request(
None,
{
"messages": [{"role": "user", "content": "hello"}],
"system": "system note",
"autodetect_images": True,
"temperature": 0.2,
},
)
assert model is None
assert kwargs["contents"] == [
"converted",
{"role": "user", "content": "hello"},
True,
]
assert kwargs["config"].kwargs["system_instruction"] == "system note"
assert "temperature" not in kwargs
def test_structured_outputs_parse_response_unwraps_adapter(
monkeypatch: pytest.MonkeyPatch,
) -> None:
handler = GenAIStructuredOutputsHandler(mode=Mode.JSON)
class FakeAdapterBase:
pass
class FakeAdapter(FakeAdapterBase):
def __init__(self, content: str) -> None:
self.content = content
monkeypatch.setattr(
"instructor.v2.providers.genai.handlers.AdapterBase",
FakeAdapterBase,
)
monkeypatch.setattr(
"instructor.v2.providers.genai.handlers.parse_genai_structured_outputs",
lambda *_args, **_kwargs: FakeAdapter("done"),
)
class FakeResponseModel(BaseModel):
pass
result = handler.parse_response(
response=SimpleNamespace(),
response_model=FakeResponseModel,
stream=False,
)
assert result == "done"