feat: add skill learning runtime
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from skills_learning.config import SkillAuthoringConfig
|
||||
from skills_learning.embeddings import embed_skill_text
|
||||
from skills_learning.models import FlowStep, FlowTemplateSkill, SkillMetadata
|
||||
from skills_learning.retrieval import retrieve_candidate_skills
|
||||
from skills_learning.store import SkillStore
|
||||
|
||||
|
||||
class FakeEmbeddingClient:
|
||||
def __init__(self, *, fail: bool = False) -> None:
|
||||
self.fail = fail
|
||||
|
||||
def embed(self, text: str, *, model: str) -> list[float]:
|
||||
if self.fail:
|
||||
raise TimeoutError("embedding timeout")
|
||||
lowered = text.lower()
|
||||
if "coffee" in lowered:
|
||||
return [1.0, 0.0]
|
||||
if "tea" in lowered:
|
||||
return [0.0, 1.0]
|
||||
return [0.5, 0.5]
|
||||
|
||||
|
||||
def _skill(name: str, goal: str) -> FlowTemplateSkill:
|
||||
return FlowTemplateSkill(
|
||||
metadata=SkillMetadata(
|
||||
name=name,
|
||||
description=f"Learned flow for {goal}",
|
||||
originating_goal=goal,
|
||||
),
|
||||
steps=[FlowStep("input_text", {"text": goal})],
|
||||
parameters={},
|
||||
)
|
||||
|
||||
|
||||
def test_embed_skill_text_degrades_to_none_on_provider_failure() -> None:
|
||||
result = embed_skill_text(
|
||||
"coffee",
|
||||
client=FakeEmbeddingClient(fail=True),
|
||||
config=SkillAuthoringConfig(enabled=True),
|
||||
)
|
||||
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_retrieve_candidate_skills_ranks_by_similarity_and_truncates_top_k() -> None:
|
||||
store = SkillStore()
|
||||
coffee = store.create_version(_skill("search coffee", "search coffee"))
|
||||
tea = store.create_version(_skill("search tea", "search tea"))
|
||||
store.store_embedding(coffee, [1.0, 0.0], model_name="fake")
|
||||
store.store_embedding(tea, [0.0, 1.0], model_name="fake")
|
||||
|
||||
results = retrieve_candidate_skills(
|
||||
"find coffee",
|
||||
store=store,
|
||||
top_k=1,
|
||||
embedding_client=FakeEmbeddingClient(),
|
||||
config=SkillAuthoringConfig(enabled=True),
|
||||
)
|
||||
|
||||
assert [result.skill.name for result in results] == ["search coffee"]
|
||||
|
||||
|
||||
def test_retrieve_candidate_skills_returns_empty_without_embeddings() -> None:
|
||||
store = SkillStore()
|
||||
store.create_version(_skill("search coffee", "search coffee"))
|
||||
|
||||
results = retrieve_candidate_skills(
|
||||
"find coffee",
|
||||
store=store,
|
||||
embedding_client=FakeEmbeddingClient(),
|
||||
config=SkillAuthoringConfig(enabled=True),
|
||||
)
|
||||
|
||||
assert results == []
|
||||
|
||||
|
||||
def test_skill_without_embedding_is_stored_but_excluded_from_retrieval() -> None:
|
||||
store = SkillStore()
|
||||
skill = store.create_version(_skill("search coffee", "search coffee"))
|
||||
|
||||
results = retrieve_candidate_skills(
|
||||
"find coffee",
|
||||
store=store,
|
||||
embedding_client=FakeEmbeddingClient(),
|
||||
config=SkillAuthoringConfig(enabled=True),
|
||||
)
|
||||
|
||||
assert store.get_by_id(skill.id) == skill
|
||||
assert results == []
|
||||
Reference in New Issue
Block a user