Add akd_ext.artifacts: Artifact model + ArtifactStore ABC#75
Merged
Conversation
- Holds path-keyed `_artifacts` dict as the in-memory index, inspired by NISH1001/ada but generalized across backends (LocalDir, GitHub, S3, DB) - Abstract methods: read_artifact, write_artifact, load_artifacts - Concrete helpers on top of the cache: refresh, list_artifacts, keys, and mapping dunders (__getitem__, __contains__, __len__, __iter__) - Also export ArtifactStore alongside Artifact in the package __init__
- Add required `root` positional to __init__ so every store has a uniform identifier (filesystem path for LocalDir, `owner/repo/path` for GitHub, `bucket/prefix` for S3, path-prefix filter for DB) - `debug` is now keyword-only after `*` - load_artifacts and refresh return Self for fluent chaining, mirroring ada's `_load_artifacts` pattern — e.g. `store = await LocalDirArtifactStore(root).load_artifacts()` - Add __setitem__ and __delitem__ for cache-only mutation; subclasses use these inside read_artifact / write_artifact to keep the cache in sync after real backend I/O
- Add `index_file: str | None = "index.md"` kwarg so stores can be configured per-backend convention (`index.md` for dev/local, `README.md` for GitHub, `SKILL.md` for Anthropic skills, `AGENT.md` for agent manifests, `None` for DB) - Add `index_for(dir_path)` helper that returns the designated overview artifact for a directory using the store's convention; tolerates leading/trailing slashes since paths are always relative to `root` - Use `PurePosixPath` for path joining inside the ABC so slug ops stay POSIX-safe across backends (no Windows backslash surprise) - Move `debug` to the last kwarg after `index_file`
- Reject clearly broken paths at model creation time: empty/whitespace, null byte, `..` segments (ambiguous traversal) - Normalize obvious oddities via PurePosixPath: strip leading `/`, collapse `//` and `./` sequences - Backend-specific path-traversal defense still belongs inside each store (e.g. LocalDirArtifactStore checking `is_relative_to(root)`) - Keeps the model contract strict enough to catch typos/footguns without over-prescribing legitimate path shapes
- Flat bulleted list of paths with descriptions where available,
matching ada's pattern: `"\n".join(f"- {k}" for k in store.keys())`
- Each line contains the exact path the caller passes to read_artifact,
no mental reconstruction from indentation needed (LLM-friendly)
- Drops straight into an f-string in the pydantic-ai system_prompt:
`f"## Available artifacts\n{store}\n\nUse read_artifact(path=...)"`
- Add 3 barebone tests covering the core ArtifactStore ops (load, read, write) via a minimal in-memory concrete subclass - Stub out akd_ext/artifacts/stores/ as an empty subpackage so future backends (LocalDir, GitHub, S3) each land in their own file and can gate optional deps independently
igaurab
approved these changes
Apr 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces
akd_ext.artifacts— a genericArtifact[T]pydantic model andArtifactStore[T]ABC that will be the foundation for dynamic, context-loading agents. Artifacts are path-keyed markdown files (or anyT-typed content) organized hierarchically; stores are pluggable backends (local filesystem, GitHub, S3, DB) that all share the same POSIX-style path semantics.This PR lands the abstractions only; concrete backends come in follow-up PRs.
The
ArtifactStoreis designed to be injected directly into a pydantic-ai agent and itsread_artifact/list_artifactsexposed as tools — so the agent can dynamically load only the context it needs at any step, instead of baking everything into the system prompt up front.Broader goal: Collaborative Agent Reasoning Engineering (CARE). SMEs + devs + helper agents co-design an agent's artifacts in akd-labs (DB-backed), then auto-publish to
nasa-impact/akd/agents/<x>/on GitHub. A dynamic-context agent in akd-ext loads those artifacts from whichever backend it's pointed at — same slug-based paths, different backend.What
Artifact[T]— generic pydantic model withpath,name,description,content: T,metadata,created_at,updated_at. Path validator rejects..segments / null bytes / empty paths and normalizes//,./, leading/.ArtifactStore[T]— ABC exposing:load_artifacts,read_artifact,write_artifact.load_artifactsandrefreshreturnSelffor fluent chaining.list_artifacts(prefix),keys(prefix),index_for(dir_path)(per-backend overview file:index.md/README.md/SKILL.md), mapping dunders (__getitem__/__setitem__/__contains__/__len__/__iter__), and__str__that renders a flat bulleted list of paths — drops straight into an LLM system prompt.strfor portability;PurePosixPathis used transiently for manipulation.akd_ext/artifacts/stores/stubbed as a subpackage so future backends each land in their own file with independent optional deps.Usage
Implementing a concrete store
Subclass
ArtifactStore[T]and implement the three abstracts. A runnable walkthrough is innotebooks/marimo/artifacts.py. Minimal in-memory example:Each concrete store interprets
rootnatively: filesystem path forLocalDir,owner/repo/sub/pathforGitHub,bucket/prefixforS3, path-prefix filter forDB. Slugs returned are always relative toroot— agent code stays backend-agnostic.Injecting into a pydantic-ai agent (follow-up, depends on #74)
Once the pydantic-ai base agent merges, the store plugs straight in:
Testing
tests/artifacts/test_base.pycovers the three core ops (load, read, write) via a minimal in-memory subclass. Also exercised interactively innotebooks/marimo/artifacts.py.Next Steps
LocalDirArtifactStore— stdlib-only, ada-style eager load.GitHubArtifactStore— via existingPyGithubdep.artifact_storeonPydanticAIBaseAgentConfig, tool auto-wiring) — depends on Add PydanticAI runtime via PydanticAIBaseAgent #74.DBArtifactStore— lives in akd-labs.