mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
709 lines
24 KiB
Python
709 lines
24 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from copy import deepcopy
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import pytest
|
|
from copilotkit import (
|
|
IntelligenceAccessDeniedError,
|
|
IntelligenceError,
|
|
IntelligenceIntegrityError,
|
|
IntelligenceUnavailableError,
|
|
)
|
|
from langchain.agents.middleware import ModelRequest
|
|
from langchain_core.language_models.fake_chat_models import FakeListChatModel
|
|
from langchain_core.messages import SystemMessage
|
|
|
|
from conftest import (
|
|
CONTAINER_ID,
|
|
FakeClock,
|
|
FakeSkillsClient,
|
|
client,
|
|
declared_contract_fields,
|
|
skill_set,
|
|
)
|
|
|
|
|
|
CORPUS_PATH = (
|
|
Path(__file__).resolve().parents[2]
|
|
/ "packages/intelligence/conformance/registry-adapters-v1.json"
|
|
)
|
|
CORPUS = json.loads(CORPUS_PATH.read_text(encoding="utf-8"))
|
|
CASES = CORPUS["cases"]
|
|
PERMANENT_DENIAL_CASES = tuple(
|
|
case for case in CASES if "permanentDenialSource" in case
|
|
)
|
|
CONSUMED_CONTRACT_FIELDS = (
|
|
"cases",
|
|
"contractVersion",
|
|
"distribution",
|
|
"fixtures",
|
|
"schemaVersion",
|
|
"sourceCorpus",
|
|
"cases[].expected",
|
|
"cases[].initialSnapshot",
|
|
"cases[].name",
|
|
"cases[].operations",
|
|
"cases[].permanentDenialSource",
|
|
"cases[].expected.calls",
|
|
"cases[].expected.genericSdk",
|
|
"cases[].expected.nativeHook",
|
|
"cases[].expected.readiness",
|
|
"cases[].expected.renderedRecords",
|
|
"cases[].expected.singleflight",
|
|
"cases[].expected.statusTransitions",
|
|
"cases[].expected.telemetryNames",
|
|
"cases[].expected.telemetryRecords",
|
|
)
|
|
|
|
|
|
def _assert_contract_structure(corpus: dict[str, Any]) -> None:
|
|
assert declared_contract_fields(corpus) == set(CONSUMED_CONTRACT_FIELDS)
|
|
assert corpus["schemaVersion"] == 1
|
|
assert corpus["contractVersion"] == "registry-adapters-v1"
|
|
assert corpus["distribution"] == {
|
|
"repositoryTestOnly": True,
|
|
"publishedExport": False,
|
|
"runtimeDependency": False,
|
|
}
|
|
assert corpus["sourceCorpus"] == "registry-sdk-v1.json"
|
|
assert isinstance(corpus["fixtures"], dict)
|
|
assert len(corpus["cases"]) == 35
|
|
|
|
case_fields = {"name", "initialSnapshot", "operations", "expected"}
|
|
expected_fields = {
|
|
"calls",
|
|
"genericSdk",
|
|
"nativeHook",
|
|
"readiness",
|
|
"renderedRecords",
|
|
"statusTransitions",
|
|
"telemetryNames",
|
|
"telemetryRecords",
|
|
}
|
|
denial_count = 0
|
|
for case in corpus["cases"]:
|
|
assert set(case) in (
|
|
case_fields,
|
|
case_fields | {"permanentDenialSource"},
|
|
)
|
|
assert set(case["expected"]) in (
|
|
expected_fields,
|
|
expected_fields | {"singleflight"},
|
|
)
|
|
denial_count += "permanentDenialSource" in case
|
|
assert denial_count == 10
|
|
|
|
|
|
_assert_contract_structure(CORPUS)
|
|
|
|
|
|
def _error_from(fields: dict[str, object]) -> IntelligenceError:
|
|
error_type = (
|
|
IntelligenceAccessDeniedError
|
|
if fields["code"] == "LEARNING_REGISTRY_DENIED"
|
|
else IntelligenceError
|
|
)
|
|
error = error_type(
|
|
str(fields.get("causeIdentity", fields["code"])),
|
|
code=str(fields["code"]),
|
|
category=str(fields["category"]),
|
|
retryable=bool(fields["retryable"]),
|
|
status=fields.get("httpStatus"),
|
|
request_id=fields.get("requestId"),
|
|
trace_id=fields.get("traceId"),
|
|
)
|
|
error.cause_identity = fields.get("causeIdentity")
|
|
return error
|
|
|
|
|
|
def _observe_error(error: BaseException) -> dict[str, object]:
|
|
observed: dict[str, object] = {
|
|
"code": error.code,
|
|
"category": error.category,
|
|
"retryable": bool(error.retryable),
|
|
}
|
|
for source, target in (
|
|
("status", "httpStatus"),
|
|
("cause_identity", "causeIdentity"),
|
|
("request_id", "requestId"),
|
|
("trace_id", "traceId"),
|
|
):
|
|
value = getattr(error, source, None)
|
|
if value is not None:
|
|
observed[target] = value
|
|
return observed
|
|
|
|
|
|
def _assert_error(error: BaseException, expected: dict[str, object]) -> None:
|
|
assert _observe_error(error) == expected
|
|
|
|
|
|
def _verified_set(
|
|
case: dict[str, Any], tmp_path: Path, label: str, *, initial: bool = False
|
|
):
|
|
expected = case["expected"]
|
|
model = (
|
|
case["initialSnapshot"] if initial else expected["genericSdk"].get("result", {})
|
|
)
|
|
records = expected["renderedRecords"]
|
|
operation_kinds = {operation["kind"] for operation in case["operations"]}
|
|
texts = tuple(record["text"] for record in records)
|
|
kwargs: dict[str, Any] = {}
|
|
if "validate-count" in operation_kinds:
|
|
texts = tuple("# Skill\n" for _ in range(129))
|
|
elif "validate-instruction-bytes" in operation_kinds:
|
|
texts = ("x" * 262145,)
|
|
elif "validate-aggregate-bytes" in operation_kinds:
|
|
texts = tuple("x" * size for size in (209715, 209715, 209715, 209715, 209717))
|
|
elif operation_kinds & {"decode-instruction", "reject-script"}:
|
|
texts = ("# Skill\n",)
|
|
elif model.get("skillCount", len(texts)) == 1 and not texts:
|
|
texts = ("# Skill\n",)
|
|
if "reject-script" in operation_kinds:
|
|
kwargs["roles"] = ("script",)
|
|
|
|
installed = skill_set(
|
|
tmp_path / label,
|
|
freshness=str(model.get("freshness", model.get("source", "fresh"))),
|
|
revoked=model.get("state") == "revoked",
|
|
texts=texts,
|
|
registry_revision=str(model.get("registryRevision") or "revision-1"),
|
|
corpus_identity=True,
|
|
**kwargs,
|
|
)
|
|
if "decode-instruction" in operation_kinds:
|
|
(installed.skill_descriptors[0].directory / "SKILL.md").write_bytes(b"\xff")
|
|
return installed
|
|
|
|
|
|
def _seed_initial_snapshot(registry, case: dict[str, Any], tmp_path: Path) -> None:
|
|
from copilotkit_intelligence_langgraph._registry_state import (
|
|
RegistrySnapshot,
|
|
_render_prompt,
|
|
render_verified_skills,
|
|
)
|
|
|
|
initial = case["initialSnapshot"]
|
|
if initial["status"] == "cold":
|
|
return
|
|
installed = None
|
|
rendered = ()
|
|
if initial["skillCount"]:
|
|
installed = _verified_set(case, tmp_path, "initial", initial=True)
|
|
rendered = render_verified_skills(
|
|
installed,
|
|
maximum_skills=128,
|
|
maximum_skill_bytes=262144,
|
|
maximum_context_bytes=1048576,
|
|
)
|
|
error = _error_from(initial["error"]) if "error" in initial else None
|
|
attempted = initial["lastAttemptAt"]
|
|
if initial["refreshDue"]:
|
|
attempted = -30000
|
|
registry._state._snapshot = RegistrySnapshot(
|
|
status=initial["status"],
|
|
source=initial["source"],
|
|
installed_skill_set=installed,
|
|
rendered_skills=rendered,
|
|
prompt=_render_prompt(rendered),
|
|
registry_revision=initial["registryRevision"],
|
|
last_attempt_at=None if attempted is None else attempted / 1000,
|
|
last_success_at=None if installed is None else 0,
|
|
error=error,
|
|
)
|
|
|
|
|
|
def _operation_outcomes(case: dict[str, Any], tmp_path: Path) -> list[object]:
|
|
expected = case["expected"]
|
|
calls = expected["calls"]["client"]
|
|
count = calls["get"] + calls["getCached"]
|
|
if count == 0:
|
|
return []
|
|
kinds = {operation["kind"] for operation in case["operations"]}
|
|
if count == 2:
|
|
return [
|
|
IntelligenceUnavailableError("transient-1"),
|
|
_verified_set(case, tmp_path, "outcome-2"),
|
|
]
|
|
if "transient-failure" in kinds:
|
|
return [IntelligenceUnavailableError("transient-1")]
|
|
if "integrity-failure" in kinds:
|
|
return [IntelligenceIntegrityError("integrity-1")]
|
|
generic = expected["genericSdk"]
|
|
adapter_validation = bool(
|
|
kinds
|
|
& {
|
|
"validate-count",
|
|
"validate-instruction-bytes",
|
|
"validate-aggregate-bytes",
|
|
"decode-instruction",
|
|
"reject-script",
|
|
}
|
|
)
|
|
if "error" in generic and not adapter_validation and "telemetry-write" not in kinds:
|
|
return [_error_from(generic["error"])]
|
|
return [_verified_set(case, tmp_path, "outcome-1")]
|
|
|
|
|
|
def _assert_permanent_denial_source(
|
|
case: dict[str, Any], outcomes: list[object]
|
|
) -> None:
|
|
source = case.get("permanentDenialSource")
|
|
if source is None:
|
|
return
|
|
|
|
initial = case["initialSnapshot"]
|
|
assert initial["status"] == "cold"
|
|
assert "error" not in initial
|
|
expected_error = case["expected"]["genericSdk"]["error"]
|
|
assert len(outcomes) == 1
|
|
outcome = outcomes[0]
|
|
assert isinstance(outcome, IntelligenceError)
|
|
assert source == expected_error["causeIdentity"] == outcome.cause_identity
|
|
assert expected_error["retryable"] is False
|
|
assert case["expected"]["statusTransitions"][-1]["to"] == "denied"
|
|
|
|
terminal = case["operations"][-1]
|
|
if source.startswith("error-category-"):
|
|
category = source.removeprefix("error-category-")
|
|
assert terminal["kind"] == "registry-error"
|
|
assert category == expected_error["category"] == outcome.category
|
|
elif source.startswith("http-"):
|
|
status = int(source.removeprefix("http-"))
|
|
assert terminal["kind"] == "http-response"
|
|
assert status == terminal["status"] == expected_error["httpStatus"]
|
|
assert outcome.status == status
|
|
else:
|
|
code_suffix = source.upper().replace("-", "_")
|
|
assert terminal["kind"] == "canonical-error"
|
|
assert expected_error["code"] == outcome.code
|
|
assert expected_error["code"].endswith(code_suffix)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"case",
|
|
PERMANENT_DENIAL_CASES,
|
|
ids=[case["permanentDenialSource"] for case in PERMANENT_DENIAL_CASES],
|
|
)
|
|
def test_permanent_denial_source_rejects_mismatched_source_mapping(
|
|
case: dict[str, Any], tmp_path: Path
|
|
) -> None:
|
|
mutated = deepcopy(case)
|
|
source = mutated["permanentDenialSource"]
|
|
terminal = mutated["operations"][-1]
|
|
if source.startswith("error-category-"):
|
|
terminal["kind"] = "canonical-error"
|
|
elif source.startswith("http-"):
|
|
terminal["status"] = 418
|
|
else:
|
|
mutated["expected"]["genericSdk"]["error"]["code"] = "LEARNING_REGISTRY_DENIED"
|
|
|
|
outcomes = _operation_outcomes(mutated, tmp_path)
|
|
with pytest.raises(AssertionError):
|
|
_assert_permanent_denial_source(mutated, outcomes)
|
|
|
|
|
|
async def _pump() -> None:
|
|
for _ in range(12):
|
|
await asyncio.sleep(0)
|
|
|
|
|
|
class _SnapshotState:
|
|
def __init__(self, snapshot, error: BaseException | None) -> None:
|
|
self._snapshot = snapshot
|
|
self._error = error
|
|
|
|
async def load(self):
|
|
if self._error is not None:
|
|
raise self._error
|
|
return self._snapshot
|
|
|
|
|
|
def _model_request() -> ModelRequest:
|
|
return ModelRequest(
|
|
model=FakeListChatModel(responses=["ok"]),
|
|
messages=[],
|
|
system_message=SystemMessage(content="base"),
|
|
tools=[],
|
|
state={"messages": []},
|
|
runtime=None,
|
|
model_settings={},
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.parametrize("case", CASES, ids=[case["name"] for case in CASES])
|
|
async def test_adapter_conformance(case: dict[str, Any], tmp_path: Path) -> None:
|
|
"""Execute every corpus operation and assert every observable adapter field."""
|
|
|
|
from copilotkit_intelligence_langgraph import createSkillRegistryMiddleware
|
|
from copilotkit_intelligence_langgraph.middleware import _SkillRegistryMiddleware
|
|
|
|
expected = case["expected"]
|
|
operations = case["operations"]
|
|
clock = FakeClock()
|
|
skills = FakeSkillsClient()
|
|
outcomes = _operation_outcomes(case, tmp_path)
|
|
_assert_permanent_denial_source(case, outcomes)
|
|
loop = asyncio.get_running_loop()
|
|
futures = [loop.create_future() for _ in outcomes]
|
|
if expected["calls"]["client"]["getCached"]:
|
|
skills.cached_outcomes.extend(futures)
|
|
else:
|
|
skills.get_outcomes.extend(futures)
|
|
|
|
telemetry: list[dict[str, object]] = []
|
|
fail_telemetry_once = "telemetry-write" in {
|
|
operation["kind"] for operation in operations
|
|
}
|
|
sink_failure = RuntimeError("sink-exception-1")
|
|
|
|
def emit(name: str, metadata: dict[str, object]) -> None:
|
|
nonlocal fail_telemetry_once
|
|
if (
|
|
fail_telemetry_once
|
|
and name == "status.changed"
|
|
and metadata.get("status") in {"ready", "revoked"}
|
|
):
|
|
fail_telemetry_once = False
|
|
raise sink_failure
|
|
telemetry.append(
|
|
{"name": name, "atMs": round(clock.seconds * 1000), "metadata": metadata}
|
|
)
|
|
|
|
registry = createSkillRegistryMiddleware(
|
|
client(skills), CONTAINER_ID, clock=clock, telemetry=emit
|
|
)
|
|
_seed_initial_snapshot(registry, case, tmp_path)
|
|
previous_status = registry.status
|
|
transitions: list[dict[str, object]] = []
|
|
active: list[asyncio.Task] = []
|
|
settled: list[object] = []
|
|
readiness_result: object | None = None
|
|
readiness_calls = 0
|
|
outcome_index = 0
|
|
close_count = 0
|
|
|
|
def observe() -> None:
|
|
nonlocal previous_status
|
|
if registry.status != previous_status:
|
|
transitions.append(
|
|
{
|
|
"atMs": round(clock.seconds * 1000),
|
|
"from": previous_status,
|
|
"to": registry.status,
|
|
}
|
|
)
|
|
previous_status = registry.status
|
|
|
|
async def settle_active() -> None:
|
|
if not active:
|
|
return
|
|
settled.extend(await asyncio.gather(*active, return_exceptions=True))
|
|
active.clear()
|
|
observe()
|
|
|
|
async def start(coroutine, *, pump: bool = True) -> None:
|
|
active.append(asyncio.create_task(coroutine))
|
|
if pump:
|
|
await _pump()
|
|
observe()
|
|
if all(task.done() for task in active):
|
|
await settle_active()
|
|
|
|
async def check_readiness(timeout: float | None = 0) -> object:
|
|
nonlocal readiness_calls
|
|
readiness_calls += 1
|
|
try:
|
|
return await registry.wait_until_ready(timeout)
|
|
except BaseException as error:
|
|
return error
|
|
|
|
for index, operation in enumerate(operations):
|
|
kind = operation["kind"]
|
|
assert kind in {
|
|
"load",
|
|
"load-caller-a",
|
|
"load-caller-b",
|
|
"cached-preload",
|
|
"registry-request",
|
|
"throttle-check",
|
|
"throttle-hit",
|
|
"close",
|
|
"readiness",
|
|
"timeout",
|
|
"projection-request",
|
|
"conditional-projection-request",
|
|
"bundle-request",
|
|
"cache-read",
|
|
"render",
|
|
"not-modified",
|
|
"changed-projection",
|
|
"revocation-observed",
|
|
"transient-failure",
|
|
"integrity-failure",
|
|
"denial-response",
|
|
"validate-count",
|
|
"validate-instruction-bytes",
|
|
"validate-aggregate-bytes",
|
|
"decode-instruction",
|
|
"reject-script",
|
|
"telemetry-write",
|
|
"registry-error",
|
|
"http-response",
|
|
"canonical-error",
|
|
}
|
|
clock.seconds = operation["atMs"] / 1000
|
|
next_kind = (
|
|
operations[index + 1]["kind"] if index + 1 < len(operations) else None
|
|
)
|
|
if kind in {"load", "load-caller-a", "load-caller-b", "registry-request"}:
|
|
await start(registry.load(), pump=next_kind != "throttle-hit")
|
|
elif kind == "cached-preload":
|
|
await start(registry.preload_cached())
|
|
elif kind == "throttle-check":
|
|
await start(registry.load())
|
|
elif kind == "throttle-hit":
|
|
await _pump()
|
|
observe()
|
|
await settle_active()
|
|
elif kind == "close":
|
|
before = registry.status
|
|
await registry.aclose()
|
|
if before != "closed":
|
|
close_count += 1
|
|
observe()
|
|
elif kind == "readiness" and next_kind != "timeout":
|
|
readiness_result = await check_readiness()
|
|
elif kind == "timeout":
|
|
readiness_result = await check_readiness(0.001)
|
|
|
|
terminal = index == len(operations) - 1 or (
|
|
kind == "transient-failure" and len(outcomes) == 2
|
|
)
|
|
if terminal and outcome_index < len(outcomes):
|
|
future = futures[outcome_index]
|
|
outcome = outcomes[outcome_index]
|
|
outcome_index += 1
|
|
if isinstance(outcome, BaseException):
|
|
future.set_exception(outcome)
|
|
else:
|
|
future.set_result(outcome)
|
|
await _pump()
|
|
await settle_active()
|
|
|
|
await settle_active()
|
|
assert outcome_index == len(outcomes)
|
|
assert all(future.done() for future in futures)
|
|
if readiness_result is None:
|
|
readiness_result = await check_readiness()
|
|
|
|
expected_readiness = expected["readiness"]
|
|
if "result" in expected_readiness:
|
|
assert not isinstance(readiness_result, BaseException)
|
|
assert readiness_result.status == expected_readiness["result"]["state"]
|
|
else:
|
|
assert isinstance(readiness_result, BaseException)
|
|
_assert_error(readiness_result, expected_readiness["error"])
|
|
assert readiness_calls == expected["calls"]["routing"]["readiness"]
|
|
|
|
client_calls = expected["calls"]["client"]
|
|
assert len(skills.get_calls) == client_calls["get"]
|
|
assert len(skills.cached_calls) == client_calls["getCached"]
|
|
kinds = [operation["kind"] for operation in operations]
|
|
observed_generic_calls = {
|
|
"projection": sum(
|
|
kind
|
|
in {
|
|
"projection-request",
|
|
"conditional-projection-request",
|
|
"registry-request",
|
|
}
|
|
for kind in kinds
|
|
),
|
|
"bundle": kinds.count("bundle-request"),
|
|
"cached": sum(kind in {"cache-read", "cached-preload"} for kind in kinds),
|
|
}
|
|
observed_generic_calls["network"] = (
|
|
observed_generic_calls["projection"] + observed_generic_calls["bundle"]
|
|
)
|
|
for field, value in observed_generic_calls.items():
|
|
assert value == client_calls[field]
|
|
|
|
expected_final_status = (
|
|
expected["statusTransitions"][-1]["to"]
|
|
if expected["statusTransitions"]
|
|
else case["initialSnapshot"]["status"]
|
|
)
|
|
assert registry.status == expected_final_status
|
|
generic = expected["genericSdk"]
|
|
if "result" in generic and "freshness" in generic["result"]:
|
|
result = generic["result"]
|
|
if result["freshness"] in {"fresh", "cached"}:
|
|
assert registry.snapshot.source == result["freshness"]
|
|
assert registry.snapshot.registry_revision == result["registryRevision"]
|
|
assert len(registry.snapshot.rendered_skills) == result["skillCount"]
|
|
assert (
|
|
sum(record.byte_length for record in registry.snapshot.rendered_skills)
|
|
== result["aggregateByteLength"]
|
|
)
|
|
elif "error" in generic:
|
|
assert isinstance(readiness_result, BaseException)
|
|
_assert_error(readiness_result, generic["error"])
|
|
|
|
assert transitions == expected["statusTransitions"]
|
|
assert [event["name"] for event in telemetry] == expected["telemetryNames"]
|
|
assert len(telemetry) == len(expected["telemetryRecords"])
|
|
for actual, wanted in zip(telemetry, expected["telemetryRecords"], strict=True):
|
|
assert actual["name"] == wanted["name"]
|
|
assert actual["atMs"] == wanted["atMs"]
|
|
actual_metadata = dict(actual["metadata"])
|
|
actual_metadata["framework"] = "fixture"
|
|
for key, value in wanted["metadata"].items():
|
|
assert actual_metadata[key] == value
|
|
|
|
actual_records = [
|
|
record.as_native() for record in registry.snapshot.rendered_skills
|
|
]
|
|
if not (kinds == ["readiness"] and case["initialSnapshot"]["status"] == "stale"):
|
|
assert actual_records == expected["renderedRecords"]
|
|
else:
|
|
assert len(actual_records) == case["initialSnapshot"]["skillCount"]
|
|
|
|
native_error = (
|
|
readiness_result if isinstance(readiness_result, BaseException) else None
|
|
)
|
|
native = _SkillRegistryMiddleware(_SnapshotState(registry.snapshot, native_error))
|
|
handler_called = False
|
|
|
|
async def handler(request: ModelRequest):
|
|
nonlocal handler_called
|
|
handler_called = True
|
|
return request
|
|
|
|
if expected["nativeHook"]["proceed"]:
|
|
forwarded = await native.awrap_model_call(_model_request(), handler)
|
|
assert handler_called
|
|
prompt = forwarded.system_message.text
|
|
cursor = -1
|
|
for record in expected["renderedRecords"]:
|
|
next_cursor = prompt.find(record["text"], cursor + 1)
|
|
assert next_cursor > cursor
|
|
assert record["skillId"] in prompt
|
|
assert record["versionId"] in prompt
|
|
cursor = next_cursor
|
|
native_hook = {"proceed": True}
|
|
else:
|
|
with pytest.raises(BaseException) as native_failure:
|
|
await native.awrap_model_call(_model_request(), handler)
|
|
assert not handler_called
|
|
_assert_error(native_failure.value, expected_readiness["error"])
|
|
native_hook = {"proceed": False}
|
|
assert expected["calls"]["routing"]["nativeHook"] == 1
|
|
|
|
if "singleflight" in expected:
|
|
assert len(skills.get_calls) == expected["singleflight"]["registryCalls"]
|
|
assert len(settled) == len(expected["singleflight"]["callers"])
|
|
assert settled[0] is settled[1]
|
|
assert settled[0].__cause__ is sink_failure
|
|
barrier = next(
|
|
operation["barrier"]
|
|
for operation in operations
|
|
if operation["kind"] == "load-caller-a"
|
|
)
|
|
singleflight = {
|
|
"barrier": barrier,
|
|
"registryCalls": len(skills.get_calls),
|
|
"sinkExceptionIdentity": str(sink_failure),
|
|
"callers": [
|
|
{
|
|
"name": operation["kind"].removeprefix("load-"),
|
|
"rejectionIdentity": result.cause_identity,
|
|
"causeIdentity": str(sink_failure),
|
|
}
|
|
for operation, result in zip(operations[:2], settled, strict=True)
|
|
],
|
|
}
|
|
elif kinds[:2] == ["load-caller-a", "load-caller-b"]:
|
|
assert len(settled) == 2
|
|
assert settled[0] is settled[1]
|
|
singleflight = None
|
|
else:
|
|
singleflight = None
|
|
|
|
attempted_load = bool(
|
|
set(kinds) & {"load", "load-caller-a", "registry-request", "cached-preload"}
|
|
)
|
|
if registry.status == "closed" and all(kind == "close" for kind in kinds):
|
|
generic_observation: dict[str, object] = {
|
|
"result": {"state": "closed", "closeCount": close_count}
|
|
}
|
|
elif registry.status in {"ready", "revoked"} or (
|
|
registry.status == "stale" and attempted_load
|
|
):
|
|
generic_observation = {
|
|
"result": {
|
|
"state": registry.status,
|
|
"freshness": (
|
|
"stale" if registry.status == "stale" else registry.snapshot.source
|
|
),
|
|
"registryRevision": registry.snapshot.registry_revision,
|
|
"skillCount": len(registry.snapshot.rendered_skills),
|
|
"aggregateByteLength": sum(
|
|
record.byte_length for record in registry.snapshot.rendered_skills
|
|
),
|
|
}
|
|
}
|
|
else:
|
|
error = (
|
|
readiness_result
|
|
if isinstance(readiness_result, BaseException)
|
|
else registry.snapshot.error
|
|
)
|
|
assert isinstance(error, BaseException)
|
|
generic_observation = {"error": _observe_error(error)}
|
|
|
|
telemetry_observation = []
|
|
for event in telemetry:
|
|
metadata = dict(event["metadata"])
|
|
assert metadata.pop("framework") == "langgraph-python"
|
|
assert metadata.pop("adapterVersion") == "0.1.0"
|
|
metadata.pop("latencyMs", None)
|
|
metadata["framework"] = "fixture"
|
|
telemetry_observation.append({**event, "metadata": metadata})
|
|
|
|
rendered_observation = (
|
|
[]
|
|
if kinds == ["readiness"] and case["initialSnapshot"]["status"] == "stale"
|
|
else actual_records
|
|
)
|
|
actual = {
|
|
"calls": {
|
|
"client": {
|
|
**observed_generic_calls,
|
|
"get": len(skills.get_calls),
|
|
"getCached": len(skills.cached_calls),
|
|
},
|
|
"routing": {"readiness": readiness_calls, "nativeHook": 1},
|
|
},
|
|
"statusTransitions": transitions,
|
|
"genericSdk": generic_observation,
|
|
"readiness": (
|
|
{"error": _observe_error(readiness_result)}
|
|
if isinstance(readiness_result, BaseException)
|
|
else {"result": {"state": readiness_result.status}}
|
|
),
|
|
"nativeHook": native_hook,
|
|
"telemetryNames": [event["name"] for event in telemetry_observation],
|
|
"renderedRecords": rendered_observation,
|
|
"telemetryRecords": telemetry_observation,
|
|
}
|
|
if singleflight is not None:
|
|
actual["singleflight"] = singleflight
|
|
|
|
assert actual == expected
|