Merge pull request #322 from ggozad/feat/reconfigure-skills

Add post-discovery reconfiguration for generated skills
This commit is contained in:
Yiorgis Gozadinos 2026-03-26 13:58:13 +02:00 committed by GitHub
commit 2e0564d7ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 38 additions and 16 deletions

View file

@ -4,6 +4,7 @@
### Added ### Added
- **Chunk visualization for generated skills**: `visualize_chunk(chunk_id)` function exposed in generated skill packages, enabling callers to render visual grounding from chunk IDs in skill state - **Chunk visualization for generated skills**: `visualize_chunk(chunk_id)` function exposed in generated skill packages, enabling callers to render visual grounding from chunk IDs in skill state
- **Configurable generated skills**: Generated skill `create_skill()` now accepts optional `db_path` and `config` parameters, enabling post-discovery reconfiguration via `skill.reconfigure()` (requires haiku.skills >= 0.11.0)
### Fixed ### Fixed

View file

@ -2,6 +2,7 @@ from pathlib import Path
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from haiku.rag.config.models import AppConfig
from haiku.skills.models import Skill from haiku.skills.models import Skill
from haiku.skills.parser import parse_skill_md from haiku.skills.parser import parse_skill_md
{% if "ask" in tool_names or "research" in tool_names %} {% if "ask" in tool_names or "research" in tool_names %}
@ -62,12 +63,21 @@ def _get_config():
return get_config() return get_config()
def create_skill() -> Skill: def create_skill(
db_path: Path | None = None,
config: AppConfig | None = None,
) -> Skill:
from haiku.rag.skills._tools import create_skill_tools from haiku.rag.skills._tools import create_skill_tools
metadata, instructions = parse_skill_md(Path(__file__).parent / "SKILL.md") metadata, instructions = parse_skill_md(Path(__file__).parent / "SKILL.md")
config = _get_config()
tools = create_skill_tools(_DB_PATH, config, SkillState, _TOOL_NAMES) if config is None:
config = _get_config()
if db_path is None:
db_path = _DB_PATH
tools = create_skill_tools(db_path, config, SkillState, _TOOL_NAMES)
return Skill( return Skill(
metadata=metadata, metadata=metadata,
instructions=instructions, instructions=instructions,

View file

@ -10,7 +10,7 @@ readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = [
"haiku.rag-slim >= {{ rag_version }}", "haiku.rag-slim >= {{ rag_version }}",
"haiku-skills >= 0.10.0", "haiku-skills >= 0.11.0",
] ]
[project.entry-points."haiku.skills"] [project.entry-points."haiku.skills"]

View file

@ -1,11 +1,11 @@
import os import os
from functools import cache from functools import cache
from pathlib import Path from pathlib import Path
from typing import Any
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from haiku.rag.agents.research.models import Citation from haiku.rag.agents.research.models import Citation
from haiku.rag.config.models import AppConfig
from haiku.rag.skills._tools import ResearchEntry from haiku.rag.skills._tools import ResearchEntry
from haiku.rag.store.models.chunk import SearchResult from haiku.rag.store.models.chunk import SearchResult
from haiku.rag.tools.document import DocumentInfo from haiku.rag.tools.document import DocumentInfo
@ -61,7 +61,7 @@ def state_metadata() -> StateMetadata:
def create_skill( def create_skill(
db_path: Path | None = None, db_path: Path | None = None,
config: Any = None, config: AppConfig | None = None,
) -> Skill: ) -> Skill:
"""Create a RAG skill for searching and analyzing documents. """Create a RAG skill for searching and analyzing documents.

View file

@ -1,10 +1,10 @@
import os import os
from functools import cache from functools import cache
from pathlib import Path from pathlib import Path
from typing import Any
from pydantic import BaseModel from pydantic import BaseModel
from haiku.rag.config.models import AppConfig
from haiku.rag.skills._tools import AnalysisEntry from haiku.rag.skills._tools import AnalysisEntry
from haiku.skills.models import Skill, SkillMetadata, SkillSource, StateMetadata from haiku.skills.models import Skill, SkillMetadata, SkillSource, StateMetadata
from haiku.skills.parser import parse_skill_md from haiku.skills.parser import parse_skill_md
@ -42,7 +42,7 @@ def state_metadata() -> StateMetadata:
def create_skill( def create_skill(
db_path: Path | None = None, db_path: Path | None = None,
config: Any = None, config: AppConfig | None = None,
) -> Skill: ) -> Skill:
"""Create an RLM analysis skill for computational document analysis. """Create an RLM analysis skill for computational document analysis.

View file

@ -24,7 +24,7 @@ classifiers = [
dependencies = [ dependencies = [
"cachetools>=7.0.2", "cachetools>=7.0.2",
"docling-core>=2.70.2", "docling-core>=2.70.2",
"haiku.skills>=0.10.0", "haiku.skills>=0.11.0",
"httpx>=0.28.1", "httpx>=0.28.1",
"jinja2>=3.1.0", "jinja2>=3.1.0",
"jsonpatch>=1.33", "jsonpatch>=1.33",

View file

@ -142,9 +142,7 @@ class TestRenderTemplates:
) )
init = tmp_path / "docs-skill" / "docs_skill" / "__init__.py" init = tmp_path / "docs-skill" / "docs_skill" / "__init__.py"
content = init.read_text() content = init.read_text()
assert ( assert "create_skill_tools(db_path, config, SkillState, _TOOL_NAMES)" in content
"create_skill_tools(_DB_PATH, config, SkillState, _TOOL_NAMES)" in content
)
def test_tool_names_in_init(self, tmp_path): def test_tool_names_in_init(self, tmp_path):
render_templates( render_templates(
@ -273,6 +271,18 @@ class TestRenderTemplates:
assert "skill_visualize_chunk" not in content assert "skill_visualize_chunk" not in content
assert "HaikuRAG" in content assert "HaikuRAG" in content
def test_create_skill_accepts_optional_params(self, tmp_path):
render_templates(
output_dir=tmp_path,
name="docs",
description="A docs skill.",
tool_names=["search"],
)
init = tmp_path / "docs-skill" / "docs_skill" / "__init__.py"
content = init.read_text()
assert "db_path: Path | None = None" in content
assert "config: AppConfig | None = None" in content
def test_generated_python_is_valid(self, tmp_path): def test_generated_python_is_valid(self, tmp_path):
render_templates( render_templates(
output_dir=tmp_path, output_dir=tmp_path,

View file

@ -318,6 +318,7 @@ dependencies = [
{ name = "jmespath" }, { name = "jmespath" },
{ name = "s3transfer" }, { name = "s3transfer" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/74/ec/636ab2aa7ad9e6bf6e297240ac2d44dba63cc6611e2d5038db318436d449/boto3-1.42.74.tar.gz", hash = "sha256:dbacd808cf2a3dadbf35f3dbd8de97b94dc9f78b1ebd439f38f552e0f9753577", size = 112739, upload-time = "2026-03-23T19:34:09.815Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/ad/16/a264b4da2af99f4a12609b93fea941cce5ec41da14b33ed3fef77a910f0c/boto3-1.42.74-py3-none-any.whl", hash = "sha256:4bf89c044d618fe4435af854ab820f09dd43569c0df15d7beb0398f50b9aa970", size = 140557, upload-time = "2026-03-23T19:34:07.084Z" }, { url = "https://files.pythonhosted.org/packages/ad/16/a264b4da2af99f4a12609b93fea941cce5ec41da14b33ed3fef77a910f0c/boto3-1.42.74-py3-none-any.whl", hash = "sha256:4bf89c044d618fe4435af854ab820f09dd43569c0df15d7beb0398f50b9aa970", size = 140557, upload-time = "2026-03-23T19:34:07.084Z" },
] ]
@ -1570,7 +1571,7 @@ requires-dist = [
{ name = "cohere", marker = "extra == 'cohere'", specifier = ">=5.20.7" }, { name = "cohere", marker = "extra == 'cohere'", specifier = ">=5.20.7" },
{ name = "docling", marker = "extra == 'docling'", specifier = ">=2.81.0" }, { name = "docling", marker = "extra == 'docling'", specifier = ">=2.81.0" },
{ name = "docling-core", specifier = ">=2.70.2" }, { name = "docling-core", specifier = ">=2.70.2" },
{ name = "haiku-skills", specifier = ">=0.10.0" }, { name = "haiku-skills", specifier = ">=0.11.0" },
{ name = "httpx", specifier = ">=0.28.1" }, { name = "httpx", specifier = ">=0.28.1" },
{ name = "jinja2", specifier = ">=3.1.0" }, { name = "jinja2", specifier = ">=3.1.0" },
{ name = "jsonpatch", specifier = ">=1.33" }, { name = "jsonpatch", specifier = ">=1.33" },
@ -1603,7 +1604,7 @@ provides-extras = ["docling", "voyageai", "mxbai", "cohere", "zeroentropy", "jin
[[package]] [[package]]
name = "haiku-skills" name = "haiku-skills"
version = "0.10.0" version = "0.11.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "ag-ui-protocol" }, { name = "ag-ui-protocol" },
@ -1613,9 +1614,9 @@ dependencies = [
{ name = "pyyaml" }, { name = "pyyaml" },
{ name = "skills-ref" }, { name = "skills-ref" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/7a/f2/97cd4c9561c402f81b118051553a808daa173f945b72ff37781ba86aaed7/haiku_skills-0.10.0.tar.gz", hash = "sha256:f7933b729088faeaa0afcae00d301b28bbdb24092f8731dfd68410b94f47b769", size = 166398, upload-time = "2026-03-24T11:17:54.608Z" } sdist = { url = "https://files.pythonhosted.org/packages/da/a7/4f8483ee3c8789cf5a2882c82545c93787cff343b78bb5a3da25f43f2bbf/haiku_skills-0.11.0.tar.gz", hash = "sha256:bb9117a3c1636ddafa4a4dda7abaed79da83c409c4428136393de0365ab4fa52", size = 166785, upload-time = "2026-03-26T11:44:38.651Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/88/0b/f2de3b43e53ccc567cde4287674c123a7f92e30862c541bcf28393e30a93/haiku_skills-0.10.0-py3-none-any.whl", hash = "sha256:5ff13d0a69bf6eecf6d6b15fbbed1176406109c626ef2e386bca4766e58b7557", size = 29445, upload-time = "2026-03-24T11:17:53.692Z" }, { url = "https://files.pythonhosted.org/packages/7a/4a/3578dbbf251ec94b894163d4859c1dcdc271d5972fec2376c1a73978d45f/haiku_skills-0.11.0-py3-none-any.whl", hash = "sha256:19c76532c6228c7abb60f06cb666a023d67b9809dd2ef8597aa0347c471a4695", size = 29664, upload-time = "2026-03-26T11:44:37.479Z" },
] ]
[[package]] [[package]]