feat: preserve governed OpenAI tool messages in 1.4.0
Add faithful single-choice chat results, tool stream activity, error-frame retries, and isolated protocol caching. Reject empty cached completions with the same value predicate as transport, add regression coverage, and adopt Apache-2.0.
This commit is contained in:
@@ -15,6 +15,7 @@ from polygateway.types import (
|
||||
ChatRequest,
|
||||
Effort,
|
||||
LLMResponse,
|
||||
OpenAIChatCompletion,
|
||||
SourceConfig,
|
||||
ThinkingObservation,
|
||||
)
|
||||
@@ -891,3 +892,113 @@ class TestCallStatsNotPoisoned:
|
||||
without = ChatRequest(messages=_MSGS)
|
||||
assert with_ctx.cache_namespace == without.cache_namespace
|
||||
assert digest_messages(with_ctx.messages) == digest_messages(without.messages)
|
||||
|
||||
|
||||
class TestOpenAICompletionCache:
|
||||
"""协议缓存不得混模式或把字典冒充公共值对象。"""
|
||||
|
||||
def test_mode_isolation_preserves_text_key(self):
|
||||
assert build_cache_key("m", _MSGS, "proj", None) == build_cache_key(
|
||||
"m", _MSGS, "proj", None, response_mode="text"
|
||||
)
|
||||
assert build_cache_key("m", _MSGS, "proj", None) != build_cache_key(
|
||||
"m", _MSGS, "proj", None, response_mode="openai"
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"completion",
|
||||
[
|
||||
None,
|
||||
{},
|
||||
{
|
||||
"message": {"content": "ok"},
|
||||
"finish_reason": "stop",
|
||||
"response_id": None,
|
||||
"usage": None,
|
||||
},
|
||||
],
|
||||
)
|
||||
async def test_missing_or_bad_completion_misses(self, completion):
|
||||
backend = InMemoryCache()
|
||||
mw = CacheMW(backend=backend, model_fingerprint="m", default_namespace="proj", ttl_s=60)
|
||||
raw = json.loads(mw._serialize(_resp()))
|
||||
raw["completion"] = completion
|
||||
assert (
|
||||
mw._rehydrate(json.dumps(raw), ChatRequest(messages=_MSGS, response_mode="openai"))
|
||||
is None
|
||||
)
|
||||
|
||||
async def test_serialization_failure_does_not_fail_call(self):
|
||||
mw = CacheMW(
|
||||
backend=InMemoryCache(), model_fingerprint="m", default_namespace="proj", ttl_s=60
|
||||
)
|
||||
response = _resp(cost=object())
|
||||
|
||||
async def terminal(_):
|
||||
return response
|
||||
|
||||
assert await mw(ChatRequest(messages=_MSGS), terminal) is response
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"message",
|
||||
[
|
||||
{"role": "assistant"},
|
||||
{"role": "assistant", "content": None, "tool_calls": []},
|
||||
{"role": "assistant", "content": "", "tool_calls": []},
|
||||
{"role": "assistant", "content": " \n", "tool_calls": []},
|
||||
{"role": "assistant", "reasoning_content": "reason only"},
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("cached_content", ["", "stale projection"])
|
||||
async def test_openai_cache_without_output_misses_and_refills(message, cached_content):
|
||||
"""空协议产物不能靠旧投影正文伪装成功;miss 后回源并覆写。"""
|
||||
backend = InMemoryCache()
|
||||
mw = _mw(backend)
|
||||
request = ChatRequest(messages=_MSGS, response_mode="openai")
|
||||
key = build_cache_key("m", _MSGS, "proj", None, response_mode="openai")
|
||||
bad = _resp(
|
||||
content=cached_content,
|
||||
completion=OpenAIChatCompletion(message, "stop", "bad", None),
|
||||
)
|
||||
await backend.set(key, mw._serialize(bad), ttl_s=60)
|
||||
fresh = _resp(
|
||||
completion=OpenAIChatCompletion(
|
||||
{"role": "assistant", "content": "cached"}, "stop", "fresh", None
|
||||
)
|
||||
)
|
||||
terminal = _Terminal(fresh)
|
||||
result = await mw(request, terminal)
|
||||
assert terminal.calls == 1 and result is fresh and not result.cache_hit
|
||||
hit = await mw(request, terminal)
|
||||
assert terminal.calls == 1 and hit.cache_hit
|
||||
assert hit.completion.response_id == "fresh"
|
||||
|
||||
|
||||
async def test_openai_tool_only_cache_hits_without_origin():
|
||||
"""有效工具本身是产物,空文本投影不妨碍命中。"""
|
||||
backend = InMemoryCache()
|
||||
mw = _mw(backend)
|
||||
completion = OpenAIChatCompletion(
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": None,
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call-1",
|
||||
"type": "function",
|
||||
"function": {"name": "echo", "arguments": "{}"},
|
||||
}
|
||||
],
|
||||
},
|
||||
"tool_calls",
|
||||
"tool-response",
|
||||
None,
|
||||
)
|
||||
request = ChatRequest(messages=_MSGS, response_mode="openai")
|
||||
key = build_cache_key("m", _MSGS, "proj", None, response_mode="openai")
|
||||
await backend.set(key, mw._serialize(_resp(content="", completion=completion)), ttl_s=60)
|
||||
terminal = _Terminal(_resp())
|
||||
hit = await mw(request, terminal)
|
||||
assert hit.cache_hit and terminal.calls == 0
|
||||
assert hit.completion == completion
|
||||
|
||||
Reference in New Issue
Block a user