Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions packages/client/src/launchdarkly_ai_server/skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,8 @@ async def get_skills(refs: Sequence[SkillReference | str]) -> list[Skill]:

Returns:
The skills found, in input order. Entries that are missing, at the
wrong version, or fail verification are omitted, and a warning logs how
many.
wrong version, or fail verification are omitted. A warning logs how
many failed verification; misses are not counted.

Raises:
TypeError: If *refs* is a single string; pass ``[key]`` instead.
Expand All @@ -295,14 +295,18 @@ async def get_skills(refs: Sequence[SkillReference | str]) -> list[Skill]:

store = require_store()

requests = list(refs)
skills: list[Skill] = []
for ref in requests:
# Only what the store served counts toward the summary: a miss or an outage
# is not a verification failure, and the summary would report it as one.
served = 0
for ref in refs:
key, wanted = reference_target(ref)
skill = resolve_from_store(store, key, wanted).skill
if skill is not None:
skills.append(skill)
log_withholding_summary("requested skills", len(requests), len(skills))
resolution = resolve_from_store(store, key, wanted)
if resolution.skill is not None:
skills.append(resolution.skill)
if resolution.reason in ("ok", "integrity_failure"):
served += 1
log_withholding_summary("requested skills the store served", served, len(skills))
return skills


Expand Down
44 changes: 44 additions & 0 deletions packages/client/tests/test_skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -1536,6 +1536,50 @@ async def test_a_key_with_no_usable_version_is_still_withheld(
assert await all_skills() == []
assert len([r for r in caplog.records if r.levelname == "WARNING"]) == 1

async def test_get_skills_for_keys_the_store_does_not_hold_is_silent(
self, store: Any, caplog: pytest.LogCaptureFixture
) -> None:
"""An empty store at boot is not an integrity problem."""
with caplog.at_level("WARNING", logger="launchdarkly_ai_server.skills_core"):
assert await get_skills(["a", "b"]) == []
assert [r for r in caplog.records if r.levelname == "WARNING"] == []

async def test_a_get_skills_pin_miss_is_silent(
self, store: Any, make_raw_skill: Any, caplog: pytest.LogCaptureFixture
) -> None:
store.put(make_raw_skill(key="a", version=2))
with caplog.at_level("WARNING", logger="launchdarkly_ai_server.skills_core"):
assert await get_skills([SkillReference(key="a", version=1)]) == []
assert [r for r in caplog.records if r.levelname == "WARNING"] == []

async def test_a_get_skills_wrong_version_answer_is_not_counted(
self, make_raw_skill: Any, caplog: pytest.LogCaptureFixture
) -> None:
skills_module._set_store(_WrongVersionAnsweringStore(make_raw_skill))
with caplog.at_level("WARNING", logger="launchdarkly_ai_server.skills_core"):
assert await get_skills([SkillReference(key="a", version=1)]) == []
assert [r for r in caplog.records if r.levelname == "WARNING"] == []

async def test_a_raising_store_is_not_counted(
self, caplog: pytest.LogCaptureFixture
) -> None:
skills_module._set_store(_RaisingStore())
with caplog.at_level("WARNING", logger="launchdarkly_ai_server.skills_core"):
assert await get_skills(["a"]) == []
assert [r for r in caplog.records if r.levelname == "WARNING"] == []

async def test_get_skills_counts_only_what_the_store_served(
self, store: Any, make_raw_skill: Any, caplog: pytest.LogCaptureFixture
) -> None:
store.put(make_raw_skill(key="good"))
store.put(self._tampered(make_raw_skill, key="bad"))
with caplog.at_level("WARNING", logger="launchdarkly_ai_server.skills_core"):
skills = await get_skills(["good", "bad", "missing"])
assert [s.key for s in skills] == ["good"]
warnings = [r for r in caplog.records if r.levelname == "WARNING"]
assert len(warnings) == 1
assert "1 of 2" in warnings[0].getMessage()

async def test_a_fully_resolved_run_is_silent(
self, store: Any, make_raw_skill: Any, caplog: pytest.LogCaptureFixture
) -> None:
Expand Down
Loading