Skip to content

feat(prompts): introduce provider-agnostic prompt provenance metadata - #155

Merged
Asaf-prog merged 2 commits into
extra-org:mainfrom
mahadbaig2:feat/prompt-provenance
Oct 30, 2026
Merged

Asaf-prog merged 2 commits into
extra-org:mainfrom
mahadbaig2:feat/prompt-provenance

Conversation

@mahadbaig2

Copy link
Copy Markdown
Contributor

Problem

As the platform expands to support remote prompt management systems such as Langfuse (issue #130), agent runs need to record the exact prompt revision and fingerprint that produced each model invocation. Storing only a prompt name or mutable label (e.g. production) makes historical executions difficult to reproduce when prompt labels change.

Proposed Changes

This PR introduces the foundational, provider-agnostic prompt provenance contract without altering existing runtime behavior:

  1. PromptProvenance Model (agent_engine.core.provenance):

    • An immutable frozen dataclass capturing source, identifier, content_hash, requested_ref, and resolved_revision.
    • Includes path normalization to ensure consistent forward-slash identifiers across platforms.
    • Provides clean dictionary serialization via to_dict().
  2. Deterministic Source Content Hashing (compute_prompt_hash):

    • Computes a deterministic SHA-256 fingerprint formatted as sha256:<digest> over the raw UTF-8 source template.
    • Distinct from rendered text: variable-substituted prompt strings and private resolver values are never included in provenance.
  3. ResolvedPrompt & Local File Loading (load_file_with_provenance):

    • Pairs raw template content with its PromptProvenance.
    • Preserves existing load_file(base_dir, relative_path) -> str behavior as a backward-compatible wrapper.
  4. Documentation & Unit Tests:

    • Documented the provenance contract in docs/PROMPT_RENDERING.md.
    • Added 22 unit tests covering deterministic hashing, immutability, path normalization, label vs. revision separation, and backward compatibility.

Scope

Part of #130. This PR intentionally focuses on establishing the typed contract, deterministic hashing, and local file loading. It does not implement Langfuse remote prompt fetching or runtime propagation, which will follow in subsequent PRs.

Verification

  • ruff format --check src tests passed.
  • ruff check src tests passed.
  • mypy src/agent_engine tests/core/test_provenance.py tests/engine/test_prompt_loading.py passed.
  • pytest tests/core tests/engine passed (145 passed).
  • agentctl validate examples/starter/agents.yaml and agentctl validate examples/enterprise-knowledge-assistant/agents.yaml passed.

Add PromptProvenance and ResolvedPrompt domain models with deterministic SHA-256 source content hashing. Introduce load_file_with_provenance in prompting module while preserving full backward compatibility for load_file.

Part of extra-org#130
@mintlify

mintlify Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Preview approval required

This pull request comes from a fork, so Mintlify won't build a preview until someone with write access approves commit 47bc125.

Approve preview

@Asaf-prog

Copy link
Copy Markdown
Collaborator

Thanks for the contribution. The provider-agnostic provenance model and the separation between source templates and rendered content look like a good foundation. Keeping runtime/trace propagation and remote prompt loading for follow-up PRs makes sense for this scope.

I found one blocker that needs to be fixed before approval: Windows-style string paths are not normalized correctly on POSIX systems.

In src/agent_engine/engine/langgraph/prompting.py:58, the loader joins base_dir / relative_path directly. On macOS/Linux, a string such as prompts\billing\system.md is treated as a filename containing backslashes, so the loader returns None even though prompts/billing/system.md exists. This causes the new test to fail:

tests/engine/test_prompt_loading.py::TestPromptFileLoadingWithProvenance::test_load_file_with_provenance_normalizes_relative_path
assert resolved is not None
E assert None is not None

There is a related issue in PromptProvenance.for_local_file() at core/provenance.py:54: PurePath(path).as_posix() leaves backslashes unchanged when given a plain string on POSIX. The existing model test uses a PureWindowsPath object, which does not cover this string-input case.

Please normalize supported path separators consistently for both file lookup and provenance identifiers, add coverage for a plain backslash-separated string passed to for_local_file(), and rerun make check.

Local validation of commit 87d7acb on macOS with Python 3.14.2:

  • Formatting, lint, and type checks passed.
  • Pytest: 1 failed, 1,480 passed.
  • make check failed at the test step.
  • make validate passed for both examples.

@mahadbaig2

Copy link
Copy Markdown
Contributor Author

Thanks for the contribution. The provider-agnostic provenance model and the separation between source templates and rendered content look like a good foundation. Keeping runtime/trace propagation and remote prompt loading for follow-up PRs makes sense for this scope.

I found one blocker that needs to be fixed before approval: Windows-style string paths are not normalized correctly on POSIX systems.

In src/agent_engine/engine/langgraph/prompting.py:58, the loader joins base_dir / relative_path directly. On macOS/Linux, a string such as prompts\billing\system.md is treated as a filename containing backslashes, so the loader returns None even though prompts/billing/system.md exists. This causes the new test to fail:

tests/engine/test_prompt_loading.py::TestPromptFileLoadingWithProvenance::test_load_file_with_provenance_normalizes_relative_path
assert resolved is not None
E assert None is not None

There is a related issue in PromptProvenance.for_local_file() at core/provenance.py:54: PurePath(path).as_posix() leaves backslashes unchanged when given a plain string on POSIX. The existing model test uses a PureWindowsPath object, which does not cover this string-input case.

Please normalize supported path separators consistently for both file lookup and provenance identifiers, add coverage for a plain backslash-separated string passed to for_local_file(), and rerun make check.

Local validation of commit 87d7acb on macOS with Python 3.14.2:

  • Formatting, lint, and type checks passed.
  • Pytest: 1 failed, 1,480 passed.
  • make check failed at the test step.
  • make validate passed for both examples.

I'll work on it and see that everything is normalized

@mahadbaig2

Copy link
Copy Markdown
Contributor Author

@Asaf-prog I've pushed a follow-up fix that normalizes Windows-style relative paths consistently for both file lookup and provenance identifiers, with additional regression tests.

I also set up Ubuntu 24.04 under WSL2 with Python 3.14.2 to reproduce and verify the issue in a POSIX environment.

Validation results:

  • Original failure reproduced on 87d7acb.
  • Previously failing test passes on 47bc125.
  • Full pytest suite: 1,485 passed, 0 failed.
  • make check: passed.
  • make validate: passed for both examples.

The fix is included in the existing PR. Ready for another review. Thanks!

@Asaf-prog
Asaf-prog merged commit 5a7c8c0 into extra-org:main Oct 30, 2026
1 check passed
@Asaf-prog

Copy link
Copy Markdown
Collaborator

please make star to the project

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants