Build the chat agent: query agent + a write tool restricted to `` /wiki/explorations/**`` and `` /output/**`` + a ``ShellTool`` advertising locally-installed Anthropic-style skills. This is the variant used by the interactive ``openkb chat`` REPL so users can iterate on genera
(
kb_dir: Path,
model: str,
language: str = "en",
)
| 104 | |
| 105 | |
| 106 | def build_chat_agent( |
| 107 | kb_dir: Path, |
| 108 | model: str, |
| 109 | language: str = "en", |
| 110 | ) -> Agent: |
| 111 | """Build the chat agent: query agent + a write tool restricted to |
| 112 | ``<kb>/wiki/explorations/**`` and ``<kb>/output/**`` + a ``ShellTool`` |
| 113 | advertising locally-installed Anthropic-style skills. |
| 114 | |
| 115 | This is the variant used by the interactive ``openkb chat`` REPL so users |
| 116 | can iterate on generated artifacts (e.g. ``output/skills/<name>/``) via |
| 117 | natural-language follow-ups without giving the agent unrestricted write |
| 118 | access to the wiki. |
| 119 | |
| 120 | Skill discovery: ``openkb/agent/skills.scan_local_skills`` looks in |
| 121 | ``<kb>/skills/``, ``~/.openkb/skills/``, ``~/.claude/skills/`` for |
| 122 | ``SKILL.md`` files. Any found skill is exposed to the agent via |
| 123 | ``ShellTool.environment.skills`` so the model can ``cat`` the skill body |
| 124 | and follow its instructions when the user's request matches. |
| 125 | """ |
| 126 | wiki_root = str(kb_dir / "wiki") |
| 127 | kb_root = str(kb_dir) |
| 128 | base = build_query_agent(wiki_root, model, language=language) |
| 129 | |
| 130 | @function_tool |
| 131 | def write_file(path: str, content: str) -> str: |
| 132 | """Write a text file under the KB. |
| 133 | |
| 134 | Allowed paths (relative to KB root): |
| 135 | * ``wiki/explorations/**`` — chat-derived notes. |
| 136 | * ``output/**`` — generator artifacts (skills, etc.). |
| 137 | |
| 138 | Any other path is rejected. Parent directories are created. |
| 139 | |
| 140 | Args: |
| 141 | path: File path relative to KB root |
| 142 | (e.g. ``"output/skills/demo/SKILL.md"``). |
| 143 | content: Full text content to write (overwrites if file exists). |
| 144 | """ |
| 145 | return write_kb_file(path, content, kb_root) |
| 146 | |
| 147 | extra_tools: list = [write_file] |
| 148 | skill_instructions_addendum = "" |
| 149 | |
| 150 | # Skill discovery via function tools. The agents SDK has a richer |
| 151 | # ``ShellTool``+``ShellToolLocalSkill`` mechanism for this, but those |
| 152 | # are OpenAI Responses-API hosted tools; LiteLLM routes through |
| 153 | # ChatCompletions which rejects hosted tools. So we use plain |
| 154 | # ``function_tool`` primitives that work with any LiteLLM-routed model. |
| 155 | from openkb.agent.skills import scan_local_skills |
| 156 | |
| 157 | skills = scan_local_skills(kb_dir) |
| 158 | skill_index = {s["name"]: s for s in skills} |
| 159 | |
| 160 | if skill_index: |
| 161 | skill_list_text = _format_skill_list(skills) |
| 162 | |
| 163 | @function_tool |
no test coverage detected