Skip to content

Comments

Prepare content templates#452

Merged
KaQuMiQ merged 1 commit intomainfrom
feature/templates
Oct 21, 2025
Merged

Prepare content templates#452
KaQuMiQ merged 1 commit intomainfrom
feature/templates

Conversation

@KaQuMiQ
Copy link
Collaborator

@KaQuMiQ KaQuMiQ commented Oct 20, 2025

No description provided.

@coderabbitai
Copy link

coderabbitai bot commented Oct 20, 2025

Warning

Rate limit exceeded

@KaQuMiQ has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 4 minutes and 14 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between a9ef300 and 410b480.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (48)
  • Makefile (1 hunks)
  • docs/guides/ComprehensiveEvaluation.md (3 hunks)
  • docs/guides/Postgres.md (3 hunks)
  • docs/guides/Templates.md (1 hunks)
  • mkdocs.yml (1 hunks)
  • pyproject.toml (1 hunks)
  • src/draive/__init__.py (2 hunks)
  • src/draive/conversation/completion/default.py (7 hunks)
  • src/draive/conversation/completion/state.py (5 hunks)
  • src/draive/conversation/completion/types.py (4 hunks)
  • src/draive/conversation/realtime/default.py (3 hunks)
  • src/draive/conversation/realtime/state.py (3 hunks)
  • src/draive/conversation/realtime/types.py (2 hunks)
  • src/draive/generation/audio/default.py (1 hunks)
  • src/draive/generation/audio/state.py (4 hunks)
  • src/draive/generation/audio/types.py (2 hunks)
  • src/draive/generation/image/default.py (1 hunks)
  • src/draive/generation/image/state.py (4 hunks)
  • src/draive/generation/image/types.py (2 hunks)
  • src/draive/generation/model/default.py (3 hunks)
  • src/draive/generation/model/state.py (4 hunks)
  • src/draive/generation/model/types.py (2 hunks)
  • src/draive/generation/text/default.py (1 hunks)
  • src/draive/generation/text/state.py (4 hunks)
  • src/draive/generation/text/types.py (2 hunks)
  • src/draive/helpers/instruction_preparation.py (3 hunks)
  • src/draive/helpers/instruction_refinement.py (5 hunks)
  • src/draive/models/__init__.py (0 hunks)
  • src/draive/models/generative.py (5 hunks)
  • src/draive/models/instructions/__init__.py (0 hunks)
  • src/draive/models/instructions/repository.py (0 hunks)
  • src/draive/models/instructions/template.py (0 hunks)
  • src/draive/models/instructions/types.py (0 hunks)
  • src/draive/multimodal/__init__.py (2 hunks)
  • src/draive/multimodal/templates/__init__.py (1 hunks)
  • src/draive/multimodal/templates/repository.py (1 hunks)
  • src/draive/multimodal/templates/types.py (1 hunks)
  • src/draive/multimodal/templates/variables.py (1 hunks)
  • src/draive/openai/images.py (3 hunks)
  • src/draive/openai/responses.py (1 hunks)
  • src/draive/postgres/__init__.py (2 hunks)
  • src/draive/postgres/instructions.py (0 hunks)
  • src/draive/postgres/memory.py (1 hunks)
  • src/draive/postgres/templates.py (1 hunks)
  • src/draive/stages/stage.py (15 hunks)
  • tests/test_generative_merge.py (1 hunks)
  • tests/test_multimodal_template_variables.py (1 hunks)
  • tests/test_toolbox.py (1 hunks)

Walkthrough

Replaces the Instructions subsystem with a Templates subsystem across the codebase. Adds Template and TemplateDeclaration types, template variable parsing/resolution utilities, in-memory/File/Postgres TemplatesRepository backends, tests for template variable handling, and documentation (Templates guide, mkdocs). Removes instruction-related modules, types, repository implementations, and public exports. Updates numerous call sites and public signatures to accept Template | ModelInstructions and to resolve via TemplatesRepository.resolve_str. Also bumps pyproject version and updates the Makefile (removed .env include, bumped UV_VERSION).

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~150 minutes

Possibly related PRs

  • Add instruction generation tool #392 — Touches src/draive/helpers/instruction_refinement.py; likely overlaps with this PR’s migration from Instructions to Template/TemplatesRepository in refinement flows.
  • Rework Stage state management #331 — Modifies src/draive/stages/stage.py; likely overlaps with this PR’s Stage method signature and instruction-resolution changes.
  • Fix Instructions resolution #370 — Refactors instruction/template resolution and storage; likely conflicts/overlaps with the new TemplatesRepository and resolution-call replacements.

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description Check ❓ Inconclusive No pull request description was provided by the author. While this PR introduces a substantial architectural change involving the removal of the Instructions system and implementation of a complete Templates system with multiple new files, repositories, and widespread integration changes, there is no description content to evaluate against the relatedness criterion. The absence of a description presents an inconclusive situation where neither a related nor completely off-topic description can be assessed. The author should add a pull request description that explains the motivation for transitioning from the Instructions system to the Templates system, describes the new architecture, highlights key files changed, and provides any relevant context for reviewers. Even a brief summary of the architectural shift and its benefits would satisfy this check.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "Prepare content templates" is directly related to the main objective of this pull request, which introduces a comprehensive new Templates system to replace the existing Instructions system. The changeset extensively implements the Template framework with TemplatesRepository, template type definitions, variable parsing and resolution, storage backends, and integrations across the codebase. While the title could be more specific about the scope (such as explicitly mentioning the replacement of Instructions), it accurately captures the primary focus and communicates the essential change clearly to reviewers scanning the project history.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
Makefile (2)

23-30: Harden uv install/update: avoid curl | sh and masked failures.

Current pipelines can succeed even when download fails (no pipefail in sh). Replace with explicit download + guarded execution.

Apply this diff to both install and update blocks:

-		echo '...installing uv...'; \
-		curl -fLsS https://astral.sh/uv/install.sh | sh; \
-		if [ $$? -ne 0 ]; then \
-			echo "...installing uv failed!"; \
-			exit 1; \
-		fi; \
+		echo '...installing uv...'; \
+		tmp="$$(mktemp)"; \
+		if ! curl -fLsS https://astral.sh/uv/install.sh -o "$$tmp"; then \
+			echo "...downloading uv installer failed!"; exit 1; \
+		fi; \
+		if ! sh "$$tmp"; then \
+			echo "...installing uv failed!"; exit 1; \
+		fi; \
+		rm -f "$$tmp"; \
-			echo '...updating uv...'; \
-			curl -fLsS https://astral.sh/uv/install.sh | sh; \
-			if [ $$? -ne 0 ]; then \
-				echo "...updating uv failed!"; \
-				exit 1; \
-			fi; \
+			echo '...updating uv...'; \
+			tmp="$$(mktemp)"; \
+			if ! curl -fLsS https://astral.sh/uv/install.sh -o "$$tmp"; then \
+				echo "...downloading uv installer failed!"; exit 1; \
+			fi; \
+			if ! sh "$$tmp"; then \
+				echo "...updating uv failed!"; exit 1; \
+			fi; \
+			rm -f "$$tmp"; \

Also applies to: 33-45


33-36: sort -V is not portable to macOS/BSD and will fail in CI on macos-latest runners.

The CI workflow explicitly tests on both ubuntu-latest and macos-latest (see .github/workflows/ci.yml). BSD sort lacks the -V flag, and the Makefile's uv_check target runs unconditionally without fallback logic, which will cause CI failures on macOS runners.

The test script output showing sort -V available was from an Ubuntu environment (which has GNU sort). This doesn't apply to macOS runners where BSD sort is the default.

Recommended fix: Either (1) gate sort -V with a portable comparator (Python, awk, or similar), (2) detect OS and use gsort if available on macOS, or (3) use a version-agnostic comparison method.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5675e21 and aaf87e6.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • Makefile (1 hunks)
  • pyproject.toml (1 hunks)
  • src/draive/multimodal/templates/repository.py (1 hunks)
  • src/draive/multimodal/templates/types.py (1 hunks)
  • src/draive/multimodal/templates/variables.py (1 hunks)
  • tests/test_multimodal_template_variables.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use Python 3.12+ features and syntax across the codebase
Format code exclusively with Ruff (make format); do not use other formatters
Skip module-level docstrings

Files:

  • tests/test_multimodal_template_variables.py
  • src/draive/multimodal/templates/variables.py
  • src/draive/multimodal/templates/types.py
  • src/draive/multimodal/templates/repository.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.py: Do not perform real network I/O in unit tests; mock providers/HTTP
Keep tests fast and focused on changed code; start with unit tests around new types/functions/adapters
Use fixtures from tests/ or add focused ones; avoid heavy integration scaffolding
Use pytest-asyncio for coroutine tests (@pytest.mark.asyncio)
Prefer scoping with ctx.scope(...) in async tests and bind required State instances explicitly
Avoid real I/O and network in async tests; stub provider calls and HTTP

Files:

  • tests/test_multimodal_template_variables.py
{pyproject.toml,pyrightconfig.json}

📄 CodeRabbit inference engine (AGENTS.md)

Use Ruff, Bandit, and Pyright (strict) via make lint

Files:

  • pyproject.toml
src/draive/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/**/*.py: Import Haiway symbols directly (from haiway import State, ctx)
Use ctx.scope(...) to bind scoped Disposables and active State; avoid global state
Route all logs through ctx.log_debug/info/warn/error; do not use print
Use latest, most strict typing syntax (Python 3.12+), with strict typing only for public APIs
Avoid loose Any except at explicit third‑party boundaries
Prefer explicit attribute access with static types; avoid dynamic getattr except at narrow boundaries
Prefer Mapping/Sequence/Iterable in public types over dict/list/set
Use final where applicable; avoid inheritance and prefer composition
Use precise unions (|) and narrow with match/isinstance; avoid cast unless provably safe and localized
Model immutable data/config and facades with haiway.State; provide ergonomic classmethods like .of(...)
Avoid in-place mutation; use State.updated(...) or functional builders to produce new instances
Access active state via haiway.ctx inside async scopes (ctx.scope(...))
Use @statemethod for public state methods that dispatch on the active instance
Log around generation calls, tool dispatch, and provider requests/responses without leaking secrets; prefer structured/concise messages
Add metrics via ctx.record where applicable
All I/O is async; keep boundaries async and use ctx.spawn for detached tasks
Use structured concurrency and valid coroutine usage; rely on haiway/asyncio; avoid custom threading
Construct multimodal content with MultimodalContent.of(...) and compose blocks explicitly
Use ResourceContent/ResourceReference for media/data blobs
Wrap custom types/data within ArtifactContent; use hidden when needed
Add NumPy-style docstrings for public symbols with Parameters/Returns/Raises and rationale when non-obvious
Avoid docstrings on internal helpers; keep names self-explanatory
Keep docstrings high-quality; mkdocstrings pulls them into API reference
Never log secrets or full request bodies containing keys/tokens

Files:

  • src/draive/multimodal/templates/variables.py
  • src/draive/multimodal/templates/types.py
  • src/draive/multimodal/templates/repository.py
src/draive/multimodal/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement content types and helpers (MultimodalContent, TextContent, ArtifactContent) under draive/multimodal/

Files:

  • src/draive/multimodal/templates/variables.py
  • src/draive/multimodal/templates/types.py
  • src/draive/multimodal/templates/repository.py
🧬 Code graph analysis (3)
tests/test_multimodal_template_variables.py (3)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/variables.py (3)
  • parse_template_variables (13-37)
  • resolve_multimodal_template (93-146)
  • resolve_text_template (40-90)
src/draive/multimodal/text.py (1)
  • TextContent (11-82)
src/draive/multimodal/templates/variables.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (2)
  • of (29-41)
  • of (78-92)
src/draive/multimodal/templates/repository.py (3)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (8)
  • Template (27-72)
  • TemplateDeclaration (76-97)
  • TemplateDefining (119-128)
  • TemplateListing (101-105)
  • TemplateLoading (109-115)
  • TemplateMissing (14-23)
  • of (29-41)
  • of (78-92)
src/draive/multimodal/templates/variables.py (3)
  • parse_template_variables (13-37)
  • resolve_multimodal_template (93-146)
  • resolve_text_template (40-90)
🔇 Additional comments (5)
Makefile (1)

12-14: UV_VERSION bump looks fine.

pyproject.toml (1)

8-8: Version bump to 0.90.0: OK.

tests/test_multimodal_template_variables.py (1)

1-203: Comprehensive and focused tests. LGTM.

Covers edge cases, error paths, and multimodal specifics without real I/O.

src/draive/multimodal/templates/repository.py (1)

440-451: Code uses correct logging API — no changes needed.

The actual haiway API method name is ctx.log_warning, not ctx.log_warn. The codebase confirms this consistently: a search across src/draive/ found 20+ instances of ctx.log_warning and zero instances of ctx.log_warn. The code at lines 440–451 correctly uses both ctx.log_warning() and ctx.log_error() as per the haiway API.

src/draive/multimodal/templates/types.py (1)

14-23: LGTM!

The exception implementation follows best practices with __slots__, clear error messaging, and proper attribute storage.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (15)
src/draive/helpers/instruction_preparation.py (2)

68-80: Unify tag naming with prompt examples (“VARIABLES” vs “TASK_VARIABLES”).

Examples in PREPARE_INSTRUCTION use …. Consider matching to reduce model confusion.

-    if not instruction.variables:
-        return "<TASK_VARIABLES>N/A</TASK_VARIABLES>"
+    if not instruction.variables:
+        return "<VARIABLES>N/A</VARIABLES>"
 ...
-    return f"<TASK_VARIABLES>\n{arguments}\n</TASK_VARIABLES>"
+    return f"<VARIABLES>\n{arguments}\n</VARIABLES>"

15-23: Add NumPy‑style docstrings for public API (exception and function).

Please document InstructionPreparationAmbiguity and prepare_instructions (Parameters/Returns/Raises, brief rationale).

As per coding guidelines.

Also applies to: 25-31

docs/guides/Postgres.md (3)

14-21: Remove PostgresInstructionsRepository from imports.

It’s no longer exported in draive.postgres and will break the example. Keep PostgresTemplatesRepository.

Apply this diff:

 from draive.postgres import (
     Postgres,
     PostgresConnectionPool,
-    PostgresConfigurationRepository,
-    PostgresInstructionsRepository,
+    PostgresConfigurationRepository,
     PostgresModelMemory,
     PostgresTemplatesRepository,
 )

25-33: Drop PostgresInstructionsRepository from the scope example and fix typo.

“use use” typo and outdated repository reference.

Apply this diff:

-    PostgresConfigurationRepository(), # use use postgres configurations
-    PostgresInstructionsRepository(), # use postgres instructions
+    PostgresConfigurationRepository(),  # use postgres configurations
     PostgresTemplatesRepository(), # use postgres templates

1-257: Remove outdated PostgresInstructionsRepository references and non-existent resolve_as_text method from docs.

The class PostgresInstructionsRepository and its corresponding resolve_as_text method no longer exist in the codebase. Update docs/guides/Postgres.md to:

  1. Remove PostgresInstructionsRepository from the imports (line 18)
  2. Remove its instantiation from the context scope (line 26)
  3. Delete the entire "InstructionsRepository implementation" section (lines 67–88)
  4. Update line 119 to remove the reference to resolve_as_text(template) method
src/draive/models/generative.py (1)

615-699: Fix signature and docstring inconsistency.

The method signature declares instructions: ModelInstructions (lines 620, 631, 642) but the docstring at line 656 claims it accepts Template | ModelInstructions. Additionally, line 657 mentions the obsolete InstructionsRepository.

Other similar methods across the codebase (e.g., src/draive/conversation/realtime/state.py, src/draive/generation/image/state.py) accept Template | ModelInstructions, suggesting this is an incomplete migration.

Apply this diff to align the signature with the docstring and other similar APIs:

     @overload
     @classmethod
     async def session(
         cls,
         *,
-        instructions: ModelInstructions = "",
+        instructions: Template | ModelInstructions = "",
         memory: ModelMemory | ModelContext = (),
         tools: ModelToolsDeclaration = ModelToolsDeclaration.none,
         output: ModelSessionOutputSelection = "auto",
         **extra: Any,
     ) -> ModelSessionScope: ...

     @overload
     async def session(
         self,
         *,
-        instructions: ModelInstructions = "",
+        instructions: Template | ModelInstructions = "",
         memory: ModelMemory | ModelContext = (),
         tools: ModelToolsDeclaration = ModelToolsDeclaration.none,
         output: ModelSessionOutputSelection = "auto",
         **extra: Any,
     ) -> ModelSessionScope: ...

     @statemethod
     async def session(
         self,
         *,
-        instructions: ModelInstructions = "",
+        instructions: Template | ModelInstructions = "",
         memory: ModelMemory | ModelContext = (),
         tools: ModelToolsDeclaration = ModelToolsDeclaration.none,
         output: ModelSessionOutputSelection = "auto",
         **extra: Any,
     ) -> ModelSessionScope:
         """Prepare and open a realtime session.

-        Resolves ``instructions`` (via ``InstructionsRepository``), normalizes the provided
-        ``memory`` into a ``ModelMemory`` if a raw context is given, and delegates to the
-        provider's ``session_preparing`` to obtain a ``ModelSessionScope``.
+        Normalizes the provided ``memory`` into a ``ModelMemory`` if a raw context is given,
+        and delegates to the provider's ``session_preparing`` to obtain a ``ModelSessionScope``.

         Parameters
         ----------
         instructions : Template | ModelInstructions, optional
-            Instructions or an instruction reference resolvable by ``InstructionsRepository``.
+            Instructions to steer the session.

Also add the Template import at the top of the file:

+from draive.multimodal import Template
src/draive/generation/model/default.py (2)

28-31: Make examples optional (non-breaking API).

Requiring examples forces all callers to pass an iterable even when unused. Default it to an empty tuple.
[seek_conciseness]

Apply:

-    examples: Iterable[tuple[MultimodalContent, Generated]],
+    examples: Iterable[tuple[MultimodalContent, Generated]] = (),

20-31: Add NumPy-style docstring for public API.

generate_model is exported via __all__; per guidelines, document Parameters/Returns/Raises.

As per coding guidelines

src/draive/conversation/realtime/default.py (1)

51-57: Pass **extra into template resolution.

Some repositories may rely on these kwargs (e.g., routing, tenancy).

Apply:

-    session_scope: ModelSessionScope = await RealtimeGenerativeModel.session(
-        instructions=await TemplatesRepository.resolve_str(instructions),
+    session_scope: ModelSessionScope = await RealtimeGenerativeModel.session(
+        instructions=await TemplatesRepository.resolve_str(instructions, **extra),
         memory=memory,
         tools=toolbox.available_tools_declaration(),
         output=output,
         **extra,
     )
src/draive/conversation/completion/default.py (3)

96-103: Template variables are not applied; arguments= kw is ignored by resolve_str.

TemplatesRepository.resolve_str does not take an arguments parameter; it only uses Template.arguments. As written, memory variables are never merged, so placeholders won’t resolve. Merge memory variables into the Template first, then resolve; mirror this in streaming.

Apply this diff:

-        result: ModelOutput = await GenerativeModel.loop(
-            instructions=instructions
-            if isinstance(instructions, str)
-            else await TemplatesRepository.resolve_str(
-                instructions,
-                arguments=memory_recall.variables,
-            ),
+        merged: str
+        if isinstance(instructions, Template):
+            # do not override explicitly provided template arguments
+            merged_template = instructions.with_arguments(
+                **{k: v for k, v in memory_recall.variables.items() if k not in instructions.arguments}
+            )
+            merged = await TemplatesRepository.resolve_str(merged_template)
+        else:
+            merged = instructions  # ModelInstructions (str)
+
+        result: ModelOutput = await GenerativeModel.loop(
+            instructions=merged,
             toolbox=toolbox,
             context=context,
             **extra,
         )

Based on coding guidelines.


143-149: Streaming path drops memory variables.

resolve_str(instructions) doesn’t incorporate memory variables. Merge them into the Template as above to keep behavior consistent.

Apply this diff:

-        async for chunk in await GenerativeModel.loop(
-            instructions=await TemplatesRepository.resolve_str(instructions),
+        # merge memory variables if a Template is used
+        resolved_instructions = (
+            await TemplatesRepository.resolve_str(
+                instructions.with_arguments(
+                    **{k: v for k, v in memory_recall.variables.items() if isinstance(instructions, Template) and k not in instructions.arguments}
+                )
+            )
+            if isinstance(instructions, Template)
+            else instructions
+        )
+
+        async for chunk in await GenerativeModel.loop(
+            instructions=resolved_instructions,
             toolbox=toolbox,
             context=context,
             stream=True,
             **extra,
         ):

Based on coding guidelines.


160-162: Asserting away tool requests may break streaming under tools.

If GenerativeModel.loop(stream=True) can yield ModelToolRequest, this assert will crash. Either handle or skip such chunks explicitly.

Example minimal handling:

-            else:
-                assert not isinstance(chunk, ModelToolRequest)  # nosec: B101
-                yield ConversationOutputChunk.of(chunk)
+            else:
+                if isinstance(chunk, ModelToolRequest):
+                    # either ignore, or surface as a dedicated chunk type
+                    continue
+                yield ConversationOutputChunk.of(chunk)

Please confirm the loop’s guarantees in this module’s contract. Based on coding guidelines.

src/draive/stages/stage.py (1)

2054-2060: Routing prompt has typos/formatting issues.

  • “Respond with with” → duplicate word.
  • Example hint lacks a closing parenthesis and is a bit awkward.

Apply this diff:

-    instructions: str = (
-        "Based on the provided context and the current result,"  # nosec: B608 - false positive
-        " select the most appropriate option from the following:"
-        f"\n\n{options_text}"
-        "\n\nRespond with with the exact option name within SELECTION xml tag"
-        f" like (e.g., '<SELECTION>{next(iter(options.keys()))}</SELECTION>'"
-    )
+    instructions: str = (
+        "Based on the provided context and the current result, "  # nosec: B608 - false positive
+        "select the most appropriate option from the following:"
+        f"\n\n{options_text}"
+        "\n\nRespond with the exact option name within a SELECTION xml tag, "
+        f"e.g., '<SELECTION>{next(iter(options.keys()))}</SELECTION>'."
+    )

Based on coding guidelines.

src/draive/helpers/instruction_refinement.py (2)

561-571: Avoid logging full refined instructions content.

This can leak sensitive/internal prompt content. Log a preview and length instead.

Apply this diff:

-    ctx.log_info(f"Prepared updated instructions using {strategy_name}")
-    ctx.log_debug(f"Updated instruction for {strategy_name}: {updated_instruction}")
+    ctx.log_info(f"Prepared updated instructions using {strategy_name}")
+    preview = (updated_instruction[:200] + "…") if len(updated_instruction) > 200 else updated_instruction
+    ctx.log_debug(
+        "Updated instruction for %s (len=%d): %s",
+        strategy_name,
+        len(updated_instruction),
+        preview,
+    )

Based on coding guidelines.


135-158: Repository patching is sound; minor enhancement suggestion.

Current patch intentionally disables defining. Consider also copying meta=repository.meta to preserve provenance for observability.

-        return TemplatesRepository(
+        return TemplatesRepository(
             listing=repository.listing,  # keep current listing
             loading=instructions_loading,  # replace loading
             # do not allow modifications - use default noop implementation
+            meta=repository.meta,
         )

Based on coding guidelines.

♻️ Duplicate comments (8)
src/draive/multimodal/templates/types.py (4)

51-62: Use Map for merged arguments to preserve immutability and type consistency.

Returning a plain dict breaks the Map-based pattern used in of() and may degrade State immutability guarantees. Replace with Map.

Apply:

         return self.__class__(
             identifier=self.identifier,
-            arguments={**self.arguments, **arguments},
+            arguments=Map({**self.arguments, **arguments}),
             meta=self.meta,
         )

Based on learnings.


30-76: Add NumPy‑style docstrings for public Template API.

Template.of, with_arguments, and with_meta are public and should include Parameters/Returns/Raises per guidelines.

As per coding guidelines.


98-101: Add NumPy‑style docstrings for TemplateDeclaration and its of() constructor.

Public symbol; document fields and constructor Parameters/Returns (short).

As per coding guidelines.


104-132: Document public runtime‑checkable protocols.

Add brief NumPy‑style docstrings describing purpose, parameters, and return types for TemplateListing, TemplateLoading, and TemplateDefining.

As per coding guidelines.

src/draive/multimodal/templates/variables.py (1)

6-10: Fix all: add resolve_text_template, remove duplicate.

Currently lists resolve_multimodal_template twice and omits resolve_text_template.

 __all__ = (
-    "parse_template_variables",
-    "resolve_multimodal_template",
-    "resolve_multimodal_template",
+    "parse_template_variables",
+    "resolve_text_template",
+    "resolve_multimodal_template",
 )
src/draive/multimodal/templates/repository.py (3)

301-305: Return templates in a stable order.

Sort declarations by identifier to avoid nondeterministic listings and flaky tests. This mirrors an earlier suggestion.

Apply this diff:

-        return tuple(self._declarations.values())
+        return tuple(sorted(self._declarations.values(), key=lambda d: d.identifier))

372-381: Return templates in a stable order (FileStorage).

Same determinism concern as above; sort by identifier.

Apply this diff:

-        assert self._declarations is not None  # nosec: B101
-        return tuple(self._declarations.values())
+        assert self._declarations is not None  # nosec: B101
+        return tuple(sorted(self._declarations.values(), key=lambda d: d.identifier))

475-486: Persist in a deterministic order to minimize config churn.

Sorting declarations before serialization makes file diffs stable.

Apply this diff:

-        file_contents: bytes = json.dumps(
-            [
-                {
-                    "identifier": declaration.identifier,
-                    "description": declaration.description,
-                    "variables": declaration.variables,
-                    "content": self._contents[declaration.identifier],
-                    "meta": declaration.meta.to_mapping(),
-                }
-                for declaration in self._declarations.values()
-            ]
-        ).encode()
+        file_contents: bytes = json.dumps(
+            [
+                {
+                    "identifier": declaration.identifier,
+                    "description": declaration.description,
+                    "variables": declaration.variables,
+                    "content": self._contents[declaration.identifier],
+                    "meta": declaration.meta.to_mapping(),
+                }
+                for declaration in sorted(self._declarations.values(), key=lambda d: d.identifier)
+            ]
+        ).encode()
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aaf87e6 and 41456ab.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (41)
  • Makefile (1 hunks)
  • docs/guides/Postgres.md (2 hunks)
  • pyproject.toml (1 hunks)
  • src/draive/__init__.py (3 hunks)
  • src/draive/conversation/completion/default.py (7 hunks)
  • src/draive/conversation/completion/state.py (5 hunks)
  • src/draive/conversation/completion/types.py (4 hunks)
  • src/draive/conversation/realtime/default.py (3 hunks)
  • src/draive/conversation/realtime/state.py (3 hunks)
  • src/draive/conversation/realtime/types.py (2 hunks)
  • src/draive/generation/audio/default.py (1 hunks)
  • src/draive/generation/audio/state.py (4 hunks)
  • src/draive/generation/audio/types.py (2 hunks)
  • src/draive/generation/image/default.py (1 hunks)
  • src/draive/generation/image/state.py (4 hunks)
  • src/draive/generation/image/types.py (2 hunks)
  • src/draive/generation/model/default.py (3 hunks)
  • src/draive/generation/model/state.py (4 hunks)
  • src/draive/generation/model/types.py (2 hunks)
  • src/draive/generation/text/default.py (1 hunks)
  • src/draive/generation/text/state.py (4 hunks)
  • src/draive/generation/text/types.py (2 hunks)
  • src/draive/helpers/instruction_preparation.py (3 hunks)
  • src/draive/helpers/instruction_refinement.py (5 hunks)
  • src/draive/models/__init__.py (0 hunks)
  • src/draive/models/generative.py (5 hunks)
  • src/draive/models/instructions/__init__.py (0 hunks)
  • src/draive/models/instructions/repository.py (0 hunks)
  • src/draive/models/instructions/template.py (0 hunks)
  • src/draive/models/instructions/types.py (0 hunks)
  • src/draive/multimodal/__init__.py (2 hunks)
  • src/draive/multimodal/templates/__init__.py (1 hunks)
  • src/draive/multimodal/templates/repository.py (1 hunks)
  • src/draive/multimodal/templates/types.py (1 hunks)
  • src/draive/multimodal/templates/variables.py (1 hunks)
  • src/draive/openai/images.py (3 hunks)
  • src/draive/postgres/__init__.py (2 hunks)
  • src/draive/postgres/instructions.py (0 hunks)
  • src/draive/postgres/templates.py (1 hunks)
  • src/draive/stages/stage.py (15 hunks)
  • tests/test_multimodal_template_variables.py (1 hunks)
💤 Files with no reviewable changes (6)
  • src/draive/models/instructions/types.py
  • src/draive/models/instructions/template.py
  • src/draive/models/init.py
  • src/draive/models/instructions/init.py
  • src/draive/postgres/instructions.py
  • src/draive/models/instructions/repository.py
🧰 Additional context used
📓 Path-based instructions (12)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use Python 3.12+ features and syntax across the codebase
Format code exclusively with Ruff (make format); do not use other formatters
Skip module-level docstrings

Files:

  • src/draive/multimodal/templates/types.py
  • src/draive/generation/audio/types.py
  • src/draive/conversation/realtime/types.py
  • src/draive/generation/text/default.py
  • src/draive/conversation/completion/state.py
  • src/draive/conversation/completion/types.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/generation/image/types.py
  • src/draive/generation/model/types.py
  • src/draive/conversation/realtime/default.py
  • src/draive/generation/model/state.py
  • src/draive/postgres/__init__.py
  • src/draive/conversation/realtime/state.py
  • src/draive/generation/audio/state.py
  • src/draive/generation/image/default.py
  • src/draive/generation/image/state.py
  • src/draive/models/generative.py
  • src/draive/postgres/templates.py
  • src/draive/__init__.py
  • tests/test_multimodal_template_variables.py
  • src/draive/generation/audio/default.py
  • src/draive/stages/stage.py
  • src/draive/conversation/completion/default.py
  • src/draive/openai/images.py
  • src/draive/generation/model/default.py
  • src/draive/multimodal/__init__.py
  • src/draive/generation/text/state.py
  • src/draive/multimodal/templates/repository.py
  • src/draive/helpers/instruction_preparation.py
  • src/draive/generation/text/types.py
  • src/draive/multimodal/templates/variables.py
  • src/draive/helpers/instruction_refinement.py
src/draive/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/**/*.py: Import Haiway symbols directly (from haiway import State, ctx)
Use ctx.scope(...) to bind scoped Disposables and active State; avoid global state
Route all logs through ctx.log_debug/info/warn/error; do not use print
Use latest, most strict typing syntax (Python 3.12+), with strict typing only for public APIs
Avoid loose Any except at explicit third‑party boundaries
Prefer explicit attribute access with static types; avoid dynamic getattr except at narrow boundaries
Prefer Mapping/Sequence/Iterable in public types over dict/list/set
Use final where applicable; avoid inheritance and prefer composition
Use precise unions (|) and narrow with match/isinstance; avoid cast unless provably safe and localized
Model immutable data/config and facades with haiway.State; provide ergonomic classmethods like .of(...)
Avoid in-place mutation; use State.updated(...) or functional builders to produce new instances
Access active state via haiway.ctx inside async scopes (ctx.scope(...))
Use @statemethod for public state methods that dispatch on the active instance
Log around generation calls, tool dispatch, and provider requests/responses without leaking secrets; prefer structured/concise messages
Add metrics via ctx.record where applicable
All I/O is async; keep boundaries async and use ctx.spawn for detached tasks
Use structured concurrency and valid coroutine usage; rely on haiway/asyncio; avoid custom threading
Construct multimodal content with MultimodalContent.of(...) and compose blocks explicitly
Use ResourceContent/ResourceReference for media/data blobs
Wrap custom types/data within ArtifactContent; use hidden when needed
Add NumPy-style docstrings for public symbols with Parameters/Returns/Raises and rationale when non-obvious
Avoid docstrings on internal helpers; keep names self-explanatory
Keep docstrings high-quality; mkdocstrings pulls them into API reference
Never log secrets or full request bodies containing keys/tokens

Files:

  • src/draive/multimodal/templates/types.py
  • src/draive/generation/audio/types.py
  • src/draive/conversation/realtime/types.py
  • src/draive/generation/text/default.py
  • src/draive/conversation/completion/state.py
  • src/draive/conversation/completion/types.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/generation/image/types.py
  • src/draive/generation/model/types.py
  • src/draive/conversation/realtime/default.py
  • src/draive/generation/model/state.py
  • src/draive/postgres/__init__.py
  • src/draive/conversation/realtime/state.py
  • src/draive/generation/audio/state.py
  • src/draive/generation/image/default.py
  • src/draive/generation/image/state.py
  • src/draive/models/generative.py
  • src/draive/postgres/templates.py
  • src/draive/__init__.py
  • src/draive/generation/audio/default.py
  • src/draive/stages/stage.py
  • src/draive/conversation/completion/default.py
  • src/draive/openai/images.py
  • src/draive/generation/model/default.py
  • src/draive/multimodal/__init__.py
  • src/draive/generation/text/state.py
  • src/draive/multimodal/templates/repository.py
  • src/draive/helpers/instruction_preparation.py
  • src/draive/generation/text/types.py
  • src/draive/multimodal/templates/variables.py
  • src/draive/helpers/instruction_refinement.py
src/draive/multimodal/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement content types and helpers (MultimodalContent, TextContent, ArtifactContent) under draive/multimodal/

Files:

  • src/draive/multimodal/templates/types.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/multimodal/__init__.py
  • src/draive/multimodal/templates/repository.py
  • src/draive/multimodal/templates/variables.py
src/draive/conversation/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement higher-level chat/realtime conversations under draive/conversation/

Files:

  • src/draive/conversation/realtime/types.py
  • src/draive/conversation/completion/state.py
  • src/draive/conversation/completion/types.py
  • src/draive/conversation/realtime/default.py
  • src/draive/conversation/realtime/state.py
  • src/draive/conversation/completion/default.py
docs/**/*

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*: When behavior/API changes, update relevant docs under docs/ and examples as applicable
When adding public APIs, update examples/guides and ensure cross-links render

Files:

  • docs/guides/Postgres.md
src/draive/{httpx,mcp,postgres,opentelemetry}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Place integrations under draive/httpx, draive/mcp, draive/postgres, draive/opentelemetry

Files:

  • src/draive/postgres/__init__.py
  • src/draive/postgres/templates.py
src/draive/models/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Keep core abstractions (GenerativeModel, tools, instructions) under draive/models/

Files:

  • src/draive/models/generative.py
src/draive/__init__.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/__init__.py: Centralize public exports in src/draive/init.py
Update src/draive/init.py exports when API surface changes

Files:

  • src/draive/__init__.py
{pyproject.toml,pyrightconfig.json}

📄 CodeRabbit inference engine (AGENTS.md)

Use Ruff, Bandit, and Pyright (strict) via make lint

Files:

  • pyproject.toml
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.py: Do not perform real network I/O in unit tests; mock providers/HTTP
Keep tests fast and focused on changed code; start with unit tests around new types/functions/adapters
Use fixtures from tests/ or add focused ones; avoid heavy integration scaffolding
Use pytest-asyncio for coroutine tests (@pytest.mark.asyncio)
Prefer scoping with ctx.scope(...) in async tests and bind required State instances explicitly
Avoid real I/O and network in async tests; stub provider calls and HTTP

Files:

  • tests/test_multimodal_template_variables.py
src/draive/stages/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement pipeline stage abstractions and helpers under draive/stages/

Files:

  • src/draive/stages/stage.py
src/draive/{openai,anthropic,mistral,gemini,vllm,ollama,bedrock,cohere}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/{openai,anthropic,mistral,gemini,vllm,ollama,bedrock,cohere}/**/*.py: Provider-specific feature modules live under their respective provider directories
Translate provider/SDK errors into typed exceptions; do not raise bare Exception and preserve context
Use environment variables for credentials and resolve via helper functions like getenv_str

Files:

  • src/draive/openai/images.py
🧠 Learnings (6)
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/**/*.py : Add NumPy-style docstrings for public symbols with Parameters/Returns/Raises and rationale when non-obvious

Applied to files:

  • src/draive/multimodal/templates/types.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/__init__.py : Centralize public exports in src/draive/__init__.py

Applied to files:

  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/__init__.py : Update src/draive/__init__.py exports when API surface changes

Applied to files:

  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/models/**/*.py : Keep core abstractions (GenerativeModel, tools, instructions) under draive/models/

Applied to files:

  • src/draive/__init__.py
  • src/draive/stages/stage.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/multimodal/**/*.py : Implement content types and helpers (MultimodalContent, TextContent, ArtifactContent) under draive/multimodal/

Applied to files:

  • src/draive/__init__.py
  • src/draive/multimodal/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/**/*.py : Import Haiway symbols directly (from haiway import State, ctx)

Applied to files:

  • src/draive/helpers/instruction_preparation.py
🧬 Code graph analysis (31)
src/draive/multimodal/templates/types.py (1)
src/draive/stages/stage.py (1)
  • with_meta (1390-1435)
src/draive/generation/audio/types.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/conversation/realtime/types.py (2)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/text/default.py (3)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (176-183)
  • resolve_str (186-193)
  • resolve_str (196-232)
src/draive/models/generative.py (5)
  • loop (185-194)
  • loop (197-206)
  • loop (210-219)
  • loop (222-231)
  • loop (234-306)
src/draive/conversation/completion/state.py (3)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/conversation/completion/types.py (2)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/__init__.py (2)
src/draive/multimodal/templates/repository.py (4)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
  • TemplatesRepository (61-294)
src/draive/multimodal/templates/types.py (6)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateDefining (123-132)
  • TemplateListing (105-109)
  • TemplateLoading (113-119)
  • TemplateMissing (18-27)
src/draive/generation/image/types.py (1)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/generation/model/types.py (3)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/conversation/realtime/default.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (176-183)
  • resolve_str (186-193)
  • resolve_str (196-232)
src/draive/generation/model/state.py (3)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/postgres/__init__.py (2)
src/draive/multimodal/templates/repository.py (3)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
src/draive/postgres/templates.py (1)
  • PostgresTemplatesRepository (28-173)
src/draive/conversation/realtime/state.py (4)
src/draive/models/types.py (2)
  • ModelMemoryRecall (719-766)
  • ModelOutput (573-672)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/audio/state.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/image/default.py (3)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-294)
  • resolve_str (176-183)
  • resolve_str (186-193)
  • resolve_str (196-232)
src/draive/openai/images.py (1)
  • generate_image (21-60)
src/draive/generation/image/state.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/postgres/templates.py (2)
src/draive/multimodal/templates/repository.py (11)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
  • TemplatesRepository (61-294)
  • listing (301-305)
  • listing (372-380)
  • load (235-250)
  • loading (307-313)
  • loading (382-392)
  • defining (315-330)
  • defining (394-413)
src/draive/multimodal/templates/types.py (1)
  • TemplateDeclaration (80-101)
src/draive/__init__.py (2)
src/draive/multimodal/templates/types.py (3)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateMissing (18-27)
src/draive/multimodal/templates/repository.py (1)
  • TemplatesRepository (61-294)
tests/test_multimodal_template_variables.py (3)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/variables.py (3)
  • parse_template_variables (13-37)
  • resolve_multimodal_template (87-134)
  • resolve_text_template (40-84)
src/draive/multimodal/text.py (1)
  • TextContent (11-82)
src/draive/generation/audio/default.py (3)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (176-183)
  • resolve_str (186-193)
  • resolve_str (196-232)
src/draive/models/generative.py (5)
  • completion (62-71)
  • completion (74-83)
  • completion (87-96)
  • completion (99-108)
  • completion (111-181)
src/draive/stages/stage.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-294)
  • resolve_str (176-183)
  • resolve_str (186-193)
  • resolve_str (196-232)
src/draive/conversation/completion/default.py (4)
src/draive/models/types.py (1)
  • ModelToolRequest (311-353)
src/draive/multimodal/artifact.py (1)
  • ArtifactContent (11-96)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-294)
  • resolve_str (176-183)
  • resolve_str (186-193)
  • resolve_str (196-232)
src/draive/openai/images.py (3)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-294)
  • resolve_str (176-183)
  • resolve_str (186-193)
  • resolve_str (196-232)
src/draive/generation/model/default.py (4)
src/draive/multimodal/templates/types.py (2)
  • Template (31-76)
  • with_arguments (51-62)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (176-183)
  • resolve_str (186-193)
  • resolve_str (196-232)
src/draive/parameters/model.py (2)
  • json_schema (443-451)
  • simplified_schema (433-440)
src/draive/parameters/schema.py (1)
  • simplified_schema (9-26)
src/draive/multimodal/__init__.py (2)
src/draive/multimodal/templates/repository.py (4)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
  • TemplatesRepository (61-294)
src/draive/multimodal/templates/types.py (6)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateDefining (123-132)
  • TemplateListing (105-109)
  • TemplateLoading (113-119)
  • TemplateMissing (18-27)
src/draive/generation/text/state.py (4)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (8)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateDefining (123-132)
  • TemplateListing (105-109)
  • TemplateLoading (113-119)
  • TemplateMissing (18-27)
  • of (33-45)
  • of (82-96)
src/draive/multimodal/templates/variables.py (3)
  • parse_template_variables (13-37)
  • resolve_multimodal_template (87-134)
  • resolve_text_template (40-84)
src/draive/postgres/templates.py (4)
  • listing (51-82)
  • loading (121-126)
  • defining (128-166)
  • load (88-119)
src/draive/helpers/instruction_preparation.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • TemplateDeclaration (80-101)
src/draive/generation/text/types.py (3)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/variables.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (2)
  • of (33-45)
  • of (82-96)
src/draive/helpers/instruction_refinement.py (5)
src/draive/multimodal/content.py (2)
  • MultimodalContent (25-592)
  • MultimodalTag (599-700)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-294)
  • loading (307-313)
  • loading (382-392)
  • load (235-250)
src/draive/multimodal/text.py (1)
  • TextContent (11-82)
src/draive/postgres/templates.py (2)
  • loading (121-126)
  • load (88-119)
🪛 GitHub Actions: CI
tests/test_multimodal_template_variables.py

[error] 1-1: Pytest failure: TypeError in resolve_text_template() takes 1 positional argument but 2 were given during test_resolve_text_template_replaces_all_placeholders. This may indicate an API/signature mismatch in the function.


[error] 1-1: Pytest failure: TypeError in resolve_text_template() takes 1 positional argument but 2 were given during multiple tests involving text template resolution.


[error] 1-1: Pytest failure: TypeError in resolve_multimodal_template() takes 1 positional argument but 2 were given during tests of multimodal template resolution.


[error] 1-1: Overall test run ended with 13 failing tests and 325 passing tests due to object signature errors in template resolution helpers.

🔇 Additional comments (36)
src/draive/multimodal/templates/types.py (1)

8-15: Exports look complete.

all now includes Template, TemplateDeclaration, TemplateDefining, TemplateListing, TemplateLoading, TemplateMissing. Good.

Makefile (1)

13-13: LGTM: bump UV_VERSION to 0.9.4.

Version guard using sort -V still correctly upgrades when current < required.

pyproject.toml (1)

8-8: Version bump acknowledged.

0.90.0 aligns with the templates migration. Ensure CHANGELOG and docs reflect public API changes.

src/draive/helpers/instruction_preparation.py (1)

25-41: Migration to TemplateDeclaration looks correct.

Type hints and instantiation path are consistent; logging uses ctx.scope/info/error as required.

Also applies to: 34-45

src/draive/multimodal/templates/variables.py (1)

40-85: Resolvers read well.

Clear missing‑argument checks and normalized assembly via MultimodalContent.of/empty. No issues spotted.

Also applies to: 87-135

src/draive/generation/audio/types.py (1)

3-7: LGTM — protocol updated to Template | ModelInstructions.

Verification confirms no outdated ResolveableInstructions usage exists in the codebase. Signature aligns with new templates flow; imports are correct and consistent with all dependent call sites.

src/draive/postgres/__init__.py (1)

12-24: LGTM — export is correct, stale symbol properly removed.

From the verification: PostgresTemplatesRepository is correctly implemented at src/draive/postgres/templates.py:28 and ready for export. PostgresInstructionsRepository is not defined anywhere in src/draive/postgres/ — it has been cleanly removed from the codebase. The references to it in docs/guides/Postgres.md are stale documentation that should be updated separately, but the export logic itself is sound.

src/draive/multimodal/__init__.py (1)

8-16: LGTM! Clean template API integration.

The new template-related imports and exports are well-structured and properly expose the Templates subsystem through the multimodal package.

Also applies to: 25-31

src/draive/generation/text/default.py (1)

6-13: LGTM! Clean migration to template-based instruction resolution.

The switch from InstructionsRepository.resolve to TemplatesRepository.resolve_str is correct, and the updated type signature properly accepts both Template and ModelInstructions (which is a string alias).

Also applies to: 20-20, 28-28

src/draive/generation/audio/default.py (1)

5-11: LGTM! Consistent template migration.

The audio generation module follows the same clean pattern as text generation, correctly migrating to Template | ModelInstructions and TemplatesRepository.resolve_str.

Also applies to: 19-19, 25-25

src/draive/generation/model/types.py (1)

4-5: LGTM! Protocol signature correctly updated.

The ModelGenerating protocol now properly accepts Template | ModelInstructions, aligning with the implementation changes across generation modules.

Also applies to: 29-29

src/draive/conversation/realtime/types.py (1)

12-13: LGTM! Realtime conversation protocol updated consistently.

The RealtimeConversationPreparing protocol correctly adopts the new Template | ModelInstructions type, maintaining consistency across the conversation layer.

Also applies to: 130-130

src/draive/generation/audio/state.py (1)

7-8: LGTM! Type annotations updated consistently.

The AudioGeneration.generate method and its overloads correctly adopt Template | ModelInstructions, maintaining consistency with the broader template migration.

Also applies to: 20-20, 29-29, 38-38

src/draive/generation/text/state.py (1)

8-9: LGTM! Type annotations updated consistently.

The TextGeneration.generate method and its overloads correctly adopt Template | ModelInstructions, completing the consistent type migration across all generation modules.

Also applies to: 20-20, 31-31, 42-42

tests/test_multimodal_template_variables.py (1)

129-203: Critical: Fix keyword-only argument calls for resolve_multimodal_template.

All calls to resolve_multimodal_template incorrectly pass arguments as a positional parameter. The function signature requires it as a keyword-only argument.

Additionally, lines 159-161 test for unused argument detection with ValueError, but the implementation snippet (lines 86-133) shows only KeyError is raised for missing arguments, with no validation for unused arguments shown.

Apply this pattern to fix all calls:

-    result = resolve_multimodal_template(template, arguments)
+    result = resolve_multimodal_template(template, arguments=arguments)

And verify the unused argument test at lines 159-161:

 def test_resolve_multimodal_template_detects_unused_argument() -> None:
-    with raises(ValueError, match="Unused template arguments provided: extra"):
-        resolve_multimodal_template("Plain text", {"extra": TextContent.of("value")})
+    # TODO: Implement unused argument detection or remove this test
+    pass

Likely an incorrect or invalid review comment.

src/draive/multimodal/templates/__init__.py (1)

1-19: LGTM!

The package exports are well-organized and consistent. All imported symbols are properly listed in __all__.

src/draive/conversation/realtime/state.py (2)

14-14: LGTM!

Import changes align with the Template system migration.

Also applies to: 22-22


37-87: LGTM!

The method signature and docstring have been updated consistently to accept Template | ModelInstructions. The instructions are correctly passed through to the preparing implementation.

src/draive/generation/image/state.py (2)

7-8: LGTM!

Import updates align with the Template system migration.


15-48: LGTM!

The method signatures are consistently updated across all overloads and the implementation.

src/draive/generation/image/types.py (1)

1-18: LGTM!

Protocol signature correctly updated to match the new Template system. Imports are appropriate.

src/draive/openai/images.py (2)

8-9: LGTM!

Import changes appropriately support the Template system migration.


21-60: LGTM!

The method signature has been updated to accept Template | ModelInstructions, and the resolution correctly uses TemplatesRepository.resolve_str to convert the instructions to a string prompt.

src/draive/generation/model/state.py (2)

8-9: LGTM!

Import changes align with the Template system migration.


16-72: LGTM!

Method signatures are consistently updated across all overloads and the implementation to accept Template | ModelInstructions.

src/draive/generation/image/default.py (2)

8-8: LGTM!

Import changes support the Template system migration.

Also applies to: 11-11


17-34: LGTM!

The function signature has been updated to accept Template | ModelInstructions, and the instructions are correctly resolved via TemplatesRepository.resolve_str before being passed to the underlying GenerativeModel.completion.

src/draive/conversation/completion/state.py (1)

32-43: Typing changes LGTM.

Expanded instructions to Template | ModelInstructions across overloads and impl without behavior changes. Default "" maintains backward compatibility.

Also applies to: 45-56, 58-68

src/draive/conversation/completion/types.py (1)

22-33: Protocol update LGTM.

Signatures now accept Template | ModelInstructions consistently across overloads and impl.

Also applies to: 34-45, 46-55

src/draive/postgres/templates.py (1)

163-166: Verify clear_cache() async behavior against haiway 0.35.3 documentation.

The code at lines 164–165 awaits clear_cache() on cached function wrappers. Without direct access to the haiway 0.35.3 source or type stubs, I cannot definitively confirm whether clear_cache() is async or sync. Please check:

  • haiway version 0.35.3 cache module source (GitHub: miquido/haiway)
  • Whether clear_cache() is defined as async def or def
  • If sync, remove await; if async, the code is correct
src/draive/stages/stage.py (4)

355-361: OK on template resolution.

Calling TemplatesRepository.resolve_str here aligns with the new template system.


447-452: OK on prompting path resolution.

Consistent with completion; no issues.


537-542: OK on loopback path resolution.

Consistent with completion; no issues.


606-611: OK on result path resolution.

Consistent with completion; no issues.

src/draive/helpers/instruction_refinement.py (2)

244-247: Good: loading initial content via TemplatesRepository.load.

This uses the new API correctly and cleanly separates identifier from content.


422-424: Nice use of a patched repository for focused evaluation.

Ensures that evaluators transparently see the refined content.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (15)
src/draive/conversation/realtime/state.py (1)

69-79: Typing: use full Generator params or Iterator to satisfy type checkers.

collections.abc.Generator requires 3 type args. Use Iterator[...] or specify Generator[Yield, Send, Return].

Apply:

-from collections.abc import Generator, Iterable
+from collections.abc import Iterator, Iterable
@@
-            def model_context_elements() -> Generator[ModelInput | ModelOutput]:
+            def model_context_elements() -> Iterator[ModelInput | ModelOutput]:
src/draive/generation/image/state.py (1)

14-23: LGTM — signatures match Template | ModelInstructions. Add brief docstrings.

Public API is consistent. Consider a short NumPy-style docstring for ImageGeneration.generate (params, returns, raises) for API docs.

src/draive/openai/images.py (1)

21-41: Bug: input is ignored when building the OpenAI image prompt. Also pass **extra to resolve_str.

Only instructions are sent as prompt, dropping user input. This degrades results. Combine resolved instructions with the textual input (and feed input as template args for Template cases). Also forward **extra to repository backends.

Apply:

@@
     async def generate_image(
         self,
         *,
-        instructions: Template | ModelInstructions,
+        instructions: Template | ModelInstructions,
         input: MultimodalContent,  # noqa: A002
         config: OpenAIImageGenerationConfig | None = None,
         **extra: Any,
     ) -> ResourceContent | ResourceReference:
@@
-            response: ImagesResponse = await self._client.images.generate(
+            # Build prompt from instructions and input
+            if isinstance(instructions, str):
+                resolved_prompt = f"{instructions}\n\n{input.to_str()}"
+            else:
+                # Provide common aliases so templates can refer to the input
+                input_text = input.to_str()
+                resolved_prompt = await TemplatesRepository.resolve_str(
+                    instructions.with_arguments(input=input_text, prompt=input_text),
+                    **extra,
+                )
+
+            response: ImagesResponse = await self._client.images.generate(
                 model=generation_config.model,
                 n=1,
-                prompt=await TemplatesRepository.resolve_str(instructions),
+                prompt=resolved_prompt,
                 quality=generation_config.quality,
                 size=generation_config.size,
                 style=generation_config.style,
                 response_format=generation_config.result,
             )
src/draive/generation/image/default.py (1)

23-29: Forward **extra to TemplatesRepository.resolve_str for backend context.

Aligns behavior across backends (e.g., Postgres/file). No semantic change otherwise.

Apply:

-        result: ModelOutput = await GenerativeModel.completion(
-            instructions=await TemplatesRepository.resolve_str(instructions),
+        result: ModelOutput = await GenerativeModel.completion(
+            instructions=await TemplatesRepository.resolve_str(
+                instructions,
+                **extra,
+            ),
src/draive/helpers/instruction_preparation.py (1)

63-66: Do not include full generated content in exceptions.

Raising ValueError(f"...: {result.to_str()}") risks leaking sensitive data to logs/telemetry.

Apply:

-        ctx.log_error("...instruction preparation failed!")
-        raise ValueError(f"Failed to prepare instruction: {result.to_str()}")
+        ctx.log_error("...instruction preparation failed!")
+        raise ValueError("Failed to prepare instruction")

Optionally emit a redacted debug log with metadata (length, has_artifacts) if needed. Based on coding guidelines.

src/draive/generation/audio/state.py (1)

14-41: Add NumPy‑style docstrings for public APIs.

AudioGeneration.generate(...) is public; add concise Parameters/Returns/Raises per guidelines. As per coding guidelines.

src/draive/generation/model/state.py (2)

23-30: Default "" not assignable to Template | ModelInstructions; include str in the union (or change the default).

Same issue as audio. Keep API consistent and type-correct.

Apply:

-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",
@@
-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",
@@
-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",

Also applies to: 38-45, 53-60


15-71: Add NumPy‑style docstrings for public API.

ModelGeneration.generate[...] is public; add Parameters/Returns/Raises. As per coding guidelines.

src/draive/conversation/completion/state.py (1)

119-136: Normalize tools before calling Toolbox.of to avoid None bug.

Passing None into Toolbox.of may raise; guard once and reuse.

Apply:

         if stream:
-            return await ctx.state(cls).completing(
-                instructions=instructions,
-                toolbox=Toolbox.of(tools),
+            toolbox_obj: Toolbox = Toolbox.of(()) if tools is None else Toolbox.of(tools)
+            return await ctx.state(cls).completing(
+                instructions=instructions,
+                toolbox=toolbox_obj,
                 memory=conversation_memory,
                 input=conversation_message,
                 stream=True,
                 **extra,
             )
         else:
-            return await ctx.state(cls).completing(
-                instructions=instructions,
-                toolbox=Toolbox.of(tools),
+            toolbox_obj: Toolbox = Toolbox.of(()) if tools is None else Toolbox.of(tools)
+            return await ctx.state(cls).completing(
+                instructions=instructions,
+                toolbox=toolbox_obj,
                 memory=conversation_memory,
                 input=conversation_message,
                 **extra,
             )
src/draive/models/generative.py (4)

34-35: Import Template and TemplatesRepository for session instruction resolution.

-from draive.multimodal import ArtifactContent, MultimodalContent, MultimodalContentPart, TextContent
+from draive.multimodal import (
+    ArtifactContent,
+    MultimodalContent,
+    MultimodalContentPart,
+    TextContent,
+    Template,
+    TemplatesRepository,
+)

615-626: Align typing with docs: accept Template | ModelInstructions in session overloads.

 async def session(
     cls,
     *,
-    instructions: ModelInstructions = "",
+    instructions: Template | ModelInstructions = "",
@@
 async def session(
     self,
     *,
-    instructions: ModelInstructions = "",
+    instructions: Template | ModelInstructions = "",
@@
 async def session(
     self,
     *,
-    instructions: ModelInstructions = "",
+    instructions: Template | ModelInstructions = "",

Also applies to: 627-637, 638-648


650-658: Doc fix: update resolution path (TemplatesRepository), drop InstructionsRepository.

-        Resolves ``instructions`` (via ``InstructionsRepository``), normalizes the provided
-        ``memory`` into a ``ModelMemory`` if a raw context is given, and delegates to the
+        Resolves ``instructions`` when a ``Template`` is provided (via
+        ``TemplatesRepository.resolve_str``), normalizes the provided ``memory`` into a
+        ``ModelMemory`` if a raw context is given, and delegates to the
         provider's ``session_preparing`` to obtain a ``ModelSessionScope``.

679-699: <!-- [scratchpad_start]
[task review_verification FINAL]
Complete analysis ready for conclusion

[key_findings_summary]

  1. REAL ISSUE: Method signature accepts only ModelInstructions (str), but docstring claims Template | ModelInstructions
  2. PATTERN MISMATCH: Codebase-wide pattern (stages, generation, conversation) all resolve Templates before downstream use
  3. MISSING IMPORTS: Template and TemplatesRepository not imported in generative.py
  4. PROPOSED FIX FLAW: instructions.with_arguments(**{k: str(v) ...}) has type mismatch - with_arguments expects Multimodal, not str
  5. MEMORY VARIABLES: Still marked as TODO; merging adds complexity not required for core fix

[verification_result]
✓ Issue exists: Template support missing from RealtimeGenerativeModel.session()
✓ Imports needed: Template, TemplatesRepository
✓ Resolution needed: Template → str via TemplatesRepository.resolve_str()
✗ Proposed memory merging: Type-incompatible (with_arguments expects Multimodal not str)

[corrected_approach]
Simpler fix: just resolve Template to str (no memory variable merging)

  • Update signature: instructions: Template | ModelInstructions = ""
  • Add imports
  • Check isinstance(instructions, Template) and resolve
  • Pass string result to session_preparing
  • Leave memory variable merging as TODO (requires separate design)

[categorization]

  • Real issue exists, but proposed fix has implementation bugs
    [done]
    -->

Fix Template and TemplatesRepository resolution; signature/implementation mismatch.

The method signature accepts only ModelInstructions but docstring and codebase pattern (stages, generation, conversation) indicate Template | ModelInstructions support. Additionally, Template is not resolved to string before passing to session_preparing, which expects ModelInstructions (str).

However, the proposed fix has a type mismatch: Template.with_arguments(**{k: str(v) ...}) expects Multimodal values, not strings. The memory variable merging also remains a TODO with unclear requirements.

Corrected approach:

+from draive.multimodal import Template, TemplatesRepository

@statemethod
async def session(
    self,
    *,
    instructions: Template | ModelInstructions = "",
    memory: ModelMemory | ModelContext = (),
    tools: ModelToolsDeclaration = ModelToolsDeclaration.none,
    output: ModelSessionOutputSelection = "auto",
    **extra: Any,
) -> ModelSessionScope:
    """..."""
    session_memory: ModelMemory
    if isinstance(memory, Sequence):
        session_memory = ModelMemory.constant(
            ModelMemoryRecall(
                context=memory,
                variables={},
                meta=META_EMPTY,
            )
        )
    else:
        session_memory = memory

+   # Resolve Template to string; pass other instruction types through
+   if isinstance(instructions, Template):
+       resolved_instructions: ModelInstructions = await TemplatesRepository.resolve_str(instructions)
+   else:
+       resolved_instructions = instructions
+
    return await self.session_preparing(
-       # TODO: FIXME: pass memory_recall.variables ??
-       instructions=instructions,
+       instructions=resolved_instructions,
        memory=session_memory,
        output=output,
        tools=tools,
        **extra,
    )

Note: Memory variable injection marked TODO; defer to separate task with proper type design.

src/draive/conversation/completion/default.py (2)

137-150: Drop invalid await before async generator; also normalize instructions like non‑stream path.

GenerativeModel.loop(..., stream=True) returns an async generator; do not await it. Also apply the same Template resolution with memory variables.

-        async for chunk in await GenerativeModel.loop(
-            instructions=await TemplatesRepository.resolve_str(instructions),
+        async for chunk in GenerativeModel.loop(
+            instructions=(
+                await TemplatesRepository.resolve_str(
+                    instructions.with_arguments(
+                        **{
+                            k: str(v)
+                            for k, v in (memory_recall.variables or {}).items()
+                            if k not in instructions.arguments
+                        }
+                    )
+                )
+                if isinstance(instructions, Template)
+                else instructions
+            ),
             toolbox=toolbox,
             context=context,
             stream=True,
             **extra,
         ):

Also applies to: 150-161


81-107: Fix template resolution to inject memory variables via .with_arguments() instead of ignored arguments= kwarg.

The resolve_str method signature accepts only template, default, and **extra parameters, meaning the arguments= kwarg is silently captured but ignored. Template has a with_arguments() method that should be used to inject variables before resolution. The current code also has an inverted isinstance check (should be Template, not str).

result: ModelOutput = await GenerativeModel.loop(
-            instructions=instructions
-            if isinstance(instructions, str)
-            else await TemplatesRepository.resolve_str(
-                instructions,
-                arguments=memory_recall.variables,
-            ),
+            instructions=(
+                await TemplatesRepository.resolve_str(
+                    instructions.with_arguments(
+                        **{
+                            k: str(v)
+                            for k, v in (memory_recall.variables or {}).items()
+                            if k not in instructions.arguments
+                        }
+                    )
+                )
+                if isinstance(instructions, Template)
+                else instructions
+            ),
             toolbox=toolbox,
             context=context,
             **extra,
         )
♻️ Duplicate comments (17)
src/draive/postgres/templates.py (1)

148-155: SQL syntax error: missing comma between $4::JSONB and $5::JSONB.

This will fail at runtime.

Apply:

             VALUES
                 (
                     $1::TEXT,
                     $2::TEXT,
                     $3::TEXT,
-                    $4::JSONB
-                    $5::JSONB
+                    $4::JSONB,
+                    $5::JSONB
                 );
docs/guides/Postgres.md (1)

119-119: Duplicate: API naming issue already flagged.

Line 119 incorrectly references resolve_as_text; the correct method name is resolve_str. This issue was already identified in a previous review comment.

src/draive/generation/text/types.py (1)

10-20: Protocol update is correct; keep docs in sync.

Type changes look good. Please add a concise docstring to TextGenerating describing parameters and return type for reference.

src/draive/generation/model/default.py (1)

35-63: Unify resolution via repository and pass **extra (handles both str and Template).

Keeps semantics consistent and lets backends use context; mirrors prior feedback.

Apply:

@@
-            case "full":
-                if isinstance(instructions, str):
-                    resolved_instructions = instructions.format(
-                        model_schema=generated.json_schema(indent=2),
-                    )
-                else:
-                    resolved_instructions = await TemplatesRepository.resolve_str(
-                        instructions.with_arguments(
-                            model_schema=generated.json_schema(indent=2),
-                        )
-                    )
+            case "full":
+                schema = generated.json_schema(indent=2)
+                if isinstance(instructions, str):
+                    resolved_instructions = await TemplatesRepository.resolve_str(
+                        Template.of("__inline__", arguments={"model_schema": schema}),
+                        default=instructions,
+                        **extra,
+                    )
+                else:
+                    resolved_instructions = await TemplatesRepository.resolve_str(
+                        instructions.with_arguments(model_schema=schema),
+                        **extra,
+                    )
@@
-            case "simplified":
-                if isinstance(instructions, str):
-                    resolved_instructions = instructions.format(
-                        model_schema=generated.simplified_schema(indent=2),
-                    )
-                else:
-                    resolved_instructions = await TemplatesRepository.resolve_str(
-                        instructions.with_arguments(
-                            model_schema=generated.simplified_schema(indent=2),
-                        )
-                    )
+            case "simplified":
+                schema = generated.simplified_schema(indent=2)
+                if isinstance(instructions, str):
+                    resolved_instructions = await TemplatesRepository.resolve_str(
+                        Template.of("__inline__", arguments={"model_schema": schema}),
+                        default=instructions,
+                        **extra,
+                    )
+                else:
+                    resolved_instructions = await TemplatesRepository.resolve_str(
+                        instructions.with_arguments(model_schema=schema),
+                        **extra,
+                    )
@@
-            case "skip":  # instruction is not modified
-                resolved_instructions = await TemplatesRepository.resolve_str(instructions)
+            case "skip":  # instruction is not modified
+                resolved_instructions = await TemplatesRepository.resolve_str(
+                    instructions,
+                    **extra,
+                )
src/draive/multimodal/templates/variables.py (1)

6-10: Fix __all__: missing resolve_text_template, duplicate entry.

Export the intended public API.

Apply:

 __all__ = (
-    "parse_template_variables",
-    "resolve_multimodal_template",
-    "resolve_multimodal_template",
+    "parse_template_variables",
+    "resolve_text_template",
+    "resolve_multimodal_template",
 )
src/draive/__init__.py (1)

289-293: Duplicate ModelInstructions in all. Remove one.

     "ModelInputInvalid",
-    "ModelInstructions",
-    "ModelInstructions",
+    "ModelInstructions",
     "ModelMemory",
src/draive/conversation/completion/default.py (1)

18-20: Import from public API (draive.models), not internal .types.

-from draive.models.types import ModelInstructions, ModelToolRequest
+from draive.models import ModelInstructions, ModelToolRequest

As per coding guidelines.

src/draive/multimodal/templates/repository.py (5)

301-306: Return stable, deterministic template order.

-        return tuple(self._declarations.values())
+        return tuple(sorted(self._declarations.values(), key=lambda d: d.identifier))

376-381: Return stable, deterministic template order (file backend).

-        return tuple(self._declarations.values())
+        return tuple(sorted(self._declarations.values(), key=lambda d: d.identifier))

213-229: Don’t rebuild the same arguments dict twice.

-        if loaded is not None:
-            return resolve_text_template(
-                loaded,
-                arguments={
-                    name: value if isinstance(value, str) else value.to_str()
-                    for name, value in template.arguments.items()
-                },
-            )
-
-        elif default is not None:
-            return resolve_text_template(
-                default,
-                arguments={
-                    name: value if isinstance(value, str) else value.to_str()
-                    for name, value in template.arguments.items()
-                },
-            )
+        args = {
+            name: (value if isinstance(value, str) else value.to_str())
+            for name, value in template.arguments.items()
+        }
+        if loaded is not None:
+            return resolve_text_template(loaded, arguments=args)
+        elif default is not None:
+            return resolve_text_template(default, arguments=args)

446-448: Use ctx.log_warn, not ctx.log_warning (would raise at runtime).

-                                ctx.log_warning(
+                                ctx.log_warn(
                                     "Invalid templates file storage element, skipping..."
                                 )

60-88: Add NumPy‑style docstrings for public API.

Public class/methods lack required docstrings (Parameters/Returns/Raises). Please document TemplatesRepository and its public methods.

Based on learnings.

Also applies to: 103-173, 174-233, 234-251, 252-290

src/draive/multimodal/templates/types.py (5)

30-76: Add NumPy-style docstrings for public API.

The Template class and its methods (of, with_arguments, with_meta) are part of the public API but still lack docstrings. This was previously flagged and remains unaddressed.

As per coding guidelines.


58-62: Use Map() for consistency and immutability.

Line 60 constructs a plain dict, but the initialization pattern (line 43) uses Map(). This inconsistency was previously flagged and remains unaddressed.

Apply this diff:

     return self.__class__(
         identifier=self.identifier,
-        arguments={**self.arguments, **arguments},
+        arguments=Map({**self.arguments, **arguments}),
         meta=self.meta,
     )

79-101: Add NumPy-style docstrings for public API.

The TemplateDeclaration class and its of() method are part of the public API but still lack docstrings. This was previously flagged and remains unaddressed.

As per coding guidelines.


91-96: Prefer Map() for consistency with immutable State pattern.

Line 94 initializes variables with a plain dict {}, inconsistent with the Map() pattern used elsewhere. This was previously flagged and remains unaddressed.

Apply this diff:

     return cls(
         identifier=identifier,
         description=description,
-        variables=variables if variables is not None else {},
+        variables=variables if variables is not None else Map(),
         meta=Meta.of(meta),
     )

104-132: Add NumPy-style docstrings for public protocols.

The runtime-checkable protocols (TemplateListing, TemplateLoading, TemplateDefining) are part of the public API and define contracts for external implementations, but still lack docstrings. This was previously flagged and remains unaddressed.

As per coding guidelines.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 41456ab and 05f22c5.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (41)
  • Makefile (1 hunks)
  • docs/guides/Postgres.md (2 hunks)
  • pyproject.toml (1 hunks)
  • src/draive/__init__.py (3 hunks)
  • src/draive/conversation/completion/default.py (7 hunks)
  • src/draive/conversation/completion/state.py (5 hunks)
  • src/draive/conversation/completion/types.py (4 hunks)
  • src/draive/conversation/realtime/default.py (3 hunks)
  • src/draive/conversation/realtime/state.py (3 hunks)
  • src/draive/conversation/realtime/types.py (2 hunks)
  • src/draive/generation/audio/default.py (1 hunks)
  • src/draive/generation/audio/state.py (4 hunks)
  • src/draive/generation/audio/types.py (2 hunks)
  • src/draive/generation/image/default.py (1 hunks)
  • src/draive/generation/image/state.py (4 hunks)
  • src/draive/generation/image/types.py (2 hunks)
  • src/draive/generation/model/default.py (3 hunks)
  • src/draive/generation/model/state.py (4 hunks)
  • src/draive/generation/model/types.py (2 hunks)
  • src/draive/generation/text/default.py (1 hunks)
  • src/draive/generation/text/state.py (4 hunks)
  • src/draive/generation/text/types.py (2 hunks)
  • src/draive/helpers/instruction_preparation.py (3 hunks)
  • src/draive/helpers/instruction_refinement.py (5 hunks)
  • src/draive/models/__init__.py (0 hunks)
  • src/draive/models/generative.py (5 hunks)
  • src/draive/models/instructions/__init__.py (0 hunks)
  • src/draive/models/instructions/repository.py (0 hunks)
  • src/draive/models/instructions/template.py (0 hunks)
  • src/draive/models/instructions/types.py (0 hunks)
  • src/draive/multimodal/__init__.py (2 hunks)
  • src/draive/multimodal/templates/__init__.py (1 hunks)
  • src/draive/multimodal/templates/repository.py (1 hunks)
  • src/draive/multimodal/templates/types.py (1 hunks)
  • src/draive/multimodal/templates/variables.py (1 hunks)
  • src/draive/openai/images.py (3 hunks)
  • src/draive/postgres/__init__.py (2 hunks)
  • src/draive/postgres/instructions.py (0 hunks)
  • src/draive/postgres/templates.py (1 hunks)
  • src/draive/stages/stage.py (15 hunks)
  • tests/test_multimodal_template_variables.py (1 hunks)
💤 Files with no reviewable changes (6)
  • src/draive/models/instructions/template.py
  • src/draive/models/init.py
  • src/draive/models/instructions/init.py
  • src/draive/models/instructions/types.py
  • src/draive/postgres/instructions.py
  • src/draive/models/instructions/repository.py
🧰 Additional context used
📓 Path-based instructions (12)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use Python 3.12+ features and syntax across the codebase
Format code exclusively with Ruff (make format); do not use other formatters
Skip module-level docstrings

Files:

  • src/draive/stages/stage.py
  • src/draive/postgres/templates.py
  • src/draive/generation/image/types.py
  • src/draive/generation/text/default.py
  • src/draive/helpers/instruction_refinement.py
  • src/draive/multimodal/__init__.py
  • src/draive/conversation/realtime/types.py
  • src/draive/generation/image/state.py
  • src/draive/openai/images.py
  • src/draive/generation/image/default.py
  • src/draive/multimodal/templates/variables.py
  • src/draive/conversation/completion/state.py
  • src/draive/generation/model/types.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/conversation/completion/default.py
  • src/draive/generation/audio/types.py
  • tests/test_multimodal_template_variables.py
  • src/draive/conversation/realtime/default.py
  • src/draive/models/generative.py
  • src/draive/generation/audio/default.py
  • src/draive/postgres/__init__.py
  • src/draive/generation/text/types.py
  • src/draive/multimodal/templates/repository.py
  • src/draive/generation/text/state.py
  • src/draive/conversation/completion/types.py
  • src/draive/conversation/realtime/state.py
  • src/draive/generation/model/default.py
  • src/draive/generation/model/state.py
  • src/draive/__init__.py
  • src/draive/generation/audio/state.py
  • src/draive/helpers/instruction_preparation.py
  • src/draive/multimodal/templates/types.py
src/draive/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/**/*.py: Import Haiway symbols directly (from haiway import State, ctx)
Use ctx.scope(...) to bind scoped Disposables and active State; avoid global state
Route all logs through ctx.log_debug/info/warn/error; do not use print
Use latest, most strict typing syntax (Python 3.12+), with strict typing only for public APIs
Avoid loose Any except at explicit third‑party boundaries
Prefer explicit attribute access with static types; avoid dynamic getattr except at narrow boundaries
Prefer Mapping/Sequence/Iterable in public types over dict/list/set
Use final where applicable; avoid inheritance and prefer composition
Use precise unions (|) and narrow with match/isinstance; avoid cast unless provably safe and localized
Model immutable data/config and facades with haiway.State; provide ergonomic classmethods like .of(...)
Avoid in-place mutation; use State.updated(...) or functional builders to produce new instances
Access active state via haiway.ctx inside async scopes (ctx.scope(...))
Use @statemethod for public state methods that dispatch on the active instance
Log around generation calls, tool dispatch, and provider requests/responses without leaking secrets; prefer structured/concise messages
Add metrics via ctx.record where applicable
All I/O is async; keep boundaries async and use ctx.spawn for detached tasks
Use structured concurrency and valid coroutine usage; rely on haiway/asyncio; avoid custom threading
Construct multimodal content with MultimodalContent.of(...) and compose blocks explicitly
Use ResourceContent/ResourceReference for media/data blobs
Wrap custom types/data within ArtifactContent; use hidden when needed
Add NumPy-style docstrings for public symbols with Parameters/Returns/Raises and rationale when non-obvious
Avoid docstrings on internal helpers; keep names self-explanatory
Keep docstrings high-quality; mkdocstrings pulls them into API reference
Never log secrets or full request bodies containing keys/tokens

Files:

  • src/draive/stages/stage.py
  • src/draive/postgres/templates.py
  • src/draive/generation/image/types.py
  • src/draive/generation/text/default.py
  • src/draive/helpers/instruction_refinement.py
  • src/draive/multimodal/__init__.py
  • src/draive/conversation/realtime/types.py
  • src/draive/generation/image/state.py
  • src/draive/openai/images.py
  • src/draive/generation/image/default.py
  • src/draive/multimodal/templates/variables.py
  • src/draive/conversation/completion/state.py
  • src/draive/generation/model/types.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/conversation/completion/default.py
  • src/draive/generation/audio/types.py
  • src/draive/conversation/realtime/default.py
  • src/draive/models/generative.py
  • src/draive/generation/audio/default.py
  • src/draive/postgres/__init__.py
  • src/draive/generation/text/types.py
  • src/draive/multimodal/templates/repository.py
  • src/draive/generation/text/state.py
  • src/draive/conversation/completion/types.py
  • src/draive/conversation/realtime/state.py
  • src/draive/generation/model/default.py
  • src/draive/generation/model/state.py
  • src/draive/__init__.py
  • src/draive/generation/audio/state.py
  • src/draive/helpers/instruction_preparation.py
  • src/draive/multimodal/templates/types.py
src/draive/stages/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement pipeline stage abstractions and helpers under draive/stages/

Files:

  • src/draive/stages/stage.py
src/draive/{httpx,mcp,postgres,opentelemetry}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Place integrations under draive/httpx, draive/mcp, draive/postgres, draive/opentelemetry

Files:

  • src/draive/postgres/templates.py
  • src/draive/postgres/__init__.py
src/draive/multimodal/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement content types and helpers (MultimodalContent, TextContent, ArtifactContent) under draive/multimodal/

Files:

  • src/draive/multimodal/__init__.py
  • src/draive/multimodal/templates/variables.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/multimodal/templates/repository.py
  • src/draive/multimodal/templates/types.py
src/draive/conversation/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement higher-level chat/realtime conversations under draive/conversation/

Files:

  • src/draive/conversation/realtime/types.py
  • src/draive/conversation/completion/state.py
  • src/draive/conversation/completion/default.py
  • src/draive/conversation/realtime/default.py
  • src/draive/conversation/completion/types.py
  • src/draive/conversation/realtime/state.py
src/draive/{openai,anthropic,mistral,gemini,vllm,ollama,bedrock,cohere}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/{openai,anthropic,mistral,gemini,vllm,ollama,bedrock,cohere}/**/*.py: Provider-specific feature modules live under their respective provider directories
Translate provider/SDK errors into typed exceptions; do not raise bare Exception and preserve context
Use environment variables for credentials and resolve via helper functions like getenv_str

Files:

  • src/draive/openai/images.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.py: Do not perform real network I/O in unit tests; mock providers/HTTP
Keep tests fast and focused on changed code; start with unit tests around new types/functions/adapters
Use fixtures from tests/ or add focused ones; avoid heavy integration scaffolding
Use pytest-asyncio for coroutine tests (@pytest.mark.asyncio)
Prefer scoping with ctx.scope(...) in async tests and bind required State instances explicitly
Avoid real I/O and network in async tests; stub provider calls and HTTP

Files:

  • tests/test_multimodal_template_variables.py
src/draive/models/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Keep core abstractions (GenerativeModel, tools, instructions) under draive/models/

Files:

  • src/draive/models/generative.py
{pyproject.toml,pyrightconfig.json}

📄 CodeRabbit inference engine (AGENTS.md)

Use Ruff, Bandit, and Pyright (strict) via make lint

Files:

  • pyproject.toml
docs/**/*

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*: When behavior/API changes, update relevant docs under docs/ and examples as applicable
When adding public APIs, update examples/guides and ensure cross-links render

Files:

  • docs/guides/Postgres.md
src/draive/__init__.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/__init__.py: Centralize public exports in src/draive/init.py
Update src/draive/init.py exports when API surface changes

Files:

  • src/draive/__init__.py
🧠 Learnings (6)
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/models/**/*.py : Keep core abstractions (GenerativeModel, tools, instructions) under draive/models/

Applied to files:

  • src/draive/stages/stage.py
  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/__init__.py : Centralize public exports in src/draive/__init__.py

Applied to files:

  • src/draive/multimodal/__init__.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/multimodal/**/*.py : Implement content types and helpers (MultimodalContent, TextContent, ArtifactContent) under draive/multimodal/

Applied to files:

  • src/draive/multimodal/__init__.py
  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/**/*.py : Add NumPy-style docstrings for public symbols with Parameters/Returns/Raises and rationale when non-obvious

Applied to files:

  • src/draive/multimodal/templates/repository.py
  • src/draive/multimodal/templates/types.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/__init__.py : Update src/draive/__init__.py exports when API surface changes

Applied to files:

  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/**/*.py : Import Haiway symbols directly (from haiway import State, ctx)

Applied to files:

  • src/draive/helpers/instruction_preparation.py
🧬 Code graph analysis (31)
src/draive/stages/stage.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (176-183)
  • resolve_str (186-193)
  • resolve_str (196-232)
src/draive/postgres/templates.py (3)
src/draive/multimodal/templates/repository.py (11)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
  • TemplatesRepository (61-294)
  • listing (301-305)
  • listing (372-380)
  • load (235-250)
  • loading (307-313)
  • loading (382-392)
  • defining (315-330)
  • defining (394-413)
src/draive/multimodal/templates/types.py (1)
  • TemplateDeclaration (80-101)
src/draive/parameters/model.py (2)
  • from_json (454-469)
  • to_json (501-517)
src/draive/generation/image/types.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/text/default.py (3)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (176-183)
  • resolve_str (186-193)
  • resolve_str (196-232)
src/draive/models/generative.py (5)
  • loop (185-194)
  • loop (197-206)
  • loop (210-219)
  • loop (222-231)
  • loop (234-306)
src/draive/helpers/instruction_refinement.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-294)
  • loading (307-313)
  • loading (382-392)
  • load (235-250)
src/draive/multimodal/__init__.py (2)
src/draive/multimodal/templates/repository.py (4)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
  • TemplatesRepository (61-294)
src/draive/multimodal/templates/types.py (6)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateDefining (123-132)
  • TemplateListing (105-109)
  • TemplateLoading (113-119)
  • TemplateMissing (18-27)
src/draive/conversation/realtime/types.py (1)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/generation/image/state.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/openai/images.py (3)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-294)
  • resolve_str (176-183)
  • resolve_str (186-193)
  • resolve_str (196-232)
src/draive/generation/image/default.py (4)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-294)
  • resolve_str (176-183)
  • resolve_str (186-193)
  • resolve_str (196-232)
src/draive/openai/images.py (1)
  • generate_image (21-60)
src/draive/conversation/completion/state.py (3)
  • completion (34-43)
  • completion (47-56)
  • completion (59-136)
src/draive/multimodal/templates/variables.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (2)
  • of (33-45)
  • of (82-96)
src/draive/conversation/completion/state.py (4)
src/draive/models/types.py (1)
  • ModelMemoryRecall (719-766)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/model/types.py (3)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/__init__.py (2)
src/draive/multimodal/templates/repository.py (4)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
  • TemplatesRepository (61-294)
src/draive/multimodal/templates/types.py (6)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateDefining (123-132)
  • TemplateListing (105-109)
  • TemplateLoading (113-119)
  • TemplateMissing (18-27)
src/draive/conversation/completion/default.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (176-183)
  • resolve_str (186-193)
  • resolve_str (196-232)
src/draive/generation/audio/types.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
tests/test_multimodal_template_variables.py (3)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/variables.py (3)
  • parse_template_variables (13-37)
  • resolve_multimodal_template (87-134)
  • resolve_text_template (40-84)
src/draive/multimodal/text.py (1)
  • TextContent (11-82)
src/draive/conversation/realtime/default.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (176-183)
  • resolve_str (186-193)
  • resolve_str (196-232)
src/draive/generation/audio/default.py (3)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (176-183)
  • resolve_str (186-193)
  • resolve_str (196-232)
src/draive/models/generative.py (5)
  • completion (62-71)
  • completion (74-83)
  • completion (87-96)
  • completion (99-108)
  • completion (111-181)
src/draive/postgres/__init__.py (2)
src/draive/multimodal/templates/repository.py (3)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
src/draive/postgres/templates.py (1)
  • PostgresTemplatesRepository (28-173)
src/draive/generation/text/types.py (3)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (8)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateDefining (123-132)
  • TemplateListing (105-109)
  • TemplateLoading (113-119)
  • TemplateMissing (18-27)
  • of (33-45)
  • of (82-96)
src/draive/multimodal/templates/variables.py (3)
  • parse_template_variables (13-37)
  • resolve_multimodal_template (87-134)
  • resolve_text_template (40-84)
src/draive/generation/text/state.py (3)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/conversation/completion/types.py (1)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/conversation/realtime/state.py (2)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/model/default.py (4)
src/draive/multimodal/templates/types.py (2)
  • Template (31-76)
  • with_arguments (51-62)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (176-183)
  • resolve_str (186-193)
  • resolve_str (196-232)
src/draive/parameters/model.py (2)
  • json_schema (443-451)
  • simplified_schema (433-440)
src/draive/parameters/schema.py (1)
  • simplified_schema (9-26)
src/draive/generation/model/state.py (2)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/__init__.py (2)
src/draive/multimodal/templates/types.py (3)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateMissing (18-27)
src/draive/multimodal/templates/repository.py (1)
  • TemplatesRepository (61-294)
src/draive/generation/audio/state.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/helpers/instruction_preparation.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • TemplateDeclaration (80-101)
src/draive/multimodal/templates/types.py (1)
src/draive/stages/stage.py (1)
  • with_meta (1390-1435)
🔇 Additional comments (27)
pyproject.toml (1)

8-8: LGTM! Version bump aligns with feature addition.

The minor version increment from 0.89.5 to 0.90.0 appropriately reflects the introduction of the Templates subsystem as a new feature.

Makefile (2)

13-13: LGTM! UV version update is a routine maintenance bump.


9-10: No action required—.env removal is intentional.

The documentation confirms that environment variables are loaded by the application code via draive.load_env(), not through the Makefile. Multiple guides consistently instruct users to create a .env file and then call load_env() during startup. No build scripts or shell configurations depend on the Makefile loading .env, so this removal does not break the build process or environment configuration workflow.

src/draive/stages/stage.py (4)

39-39: LGTM! Import updates align with Templates subsystem.

The imports correctly add ModelInstructions and the new Template and TemplatesRepository types, supporting the transition from the Instructions subsystem to Templates.

Also applies to: 49-49


293-356: LGTM! Consistent Template | ModelInstructions handling in completion stage.

The completion method correctly:

  • Accepts instructions as Template | ModelInstructions
  • Resolves templates via await TemplatesRepository.resolve_str(instructions) before passing to GenerativeModel.loop
  • Handles both template resolution and plain string instructions uniformly

380-447: LGTM! Consistent template resolution across all completion variants.

The prompting_completion, loopback_completion, and result_completion methods all follow the same pattern:

  • Accept instructions: Template | ModelInstructions
  • Resolve via await TemplatesRepository.resolve_str(instructions)
  • Pass resolved string to GenerativeModel.loop

This consistency ensures uniform template handling across all stage completion operations.

Also applies to: 469-537, 559-606


2045-2093: LGTM! Model routing correctly resolves instructions.

The _model_routing helper constructs routing instructions as a plain string and correctly resolves it via await TemplatesRepository.resolve_str(instructions) at line 2075 before passing to GenerativeModel.loop.

src/draive/helpers/instruction_refinement.py (4)

13-20: LGTM! Import updates align with Templates subsystem.

The imports correctly transition from instruction-centric types to the new Template-based types, including Template and TemplatesRepository.


27-100: LGTM! Function signature updated for Templates subsystem.

The refine_instructions function correctly:

  • Accepts instructions: Template parameter
  • Handles optional instructions_content: ModelInstructions | None for pre-loaded content
  • Returns ModelInstructions (the refined string content)

This aligns with the broader Templates subsystem refactor.


135-158: LGTM! Template interception pattern correctly implemented.

The patched_instructions_repository property cleverly intercepts template loading to inject refined instructions during evaluation:

  • Retrieves the active TemplatesRepository from context
  • Wraps the loading method to return updated content when the identifier matches
  • Falls back to original repository loading for other templates
  • Preserves listing and disables defining (appropriate for evaluation contexts)

This enables evaluating refined instructions without persisting them.


244-249: LGTM! Correct use of TemplatesRepository.load.

When instructions_content is not provided, the code correctly uses await TemplatesRepository.load(instructions) to fetch the template content.

src/draive/generation/audio/types.py (2)

3-4: LGTM! Import updates align with Templates subsystem.


15-15: LGTM! Protocol signature updated for Template support.

The AudioGenerating protocol now accepts instructions: Template | ModelInstructions, consistent with the broader Templates subsystem refactor across other generation modules.

src/draive/multimodal/__init__.py (2)

8-16: LGTM! Template subsystem exports added.

The imports correctly expose the new Template-related types from the draive.multimodal.templates submodule.


25-31: LGTM! Public API expansion for Templates subsystem.

The __all__ list correctly includes all Template-related types (Template, TemplateDeclaration, TemplateDefining, TemplateListing, TemplateLoading, TemplateMissing, TemplatesRepository), making them part of the public multimodal API surface. This aligns with the coding guideline to centralize public exports.

Based on learnings.

src/draive/generation/text/default.py (2)

6-13: LGTM! Import updates align with Templates subsystem.

The imports correctly add ModelInstructions, Template, and TemplatesRepository, replacing the old Instructions-based imports.


18-28: LGTM! Consistent Template | ModelInstructions handling.

The generate_text function correctly:

  • Accepts instructions: Template | ModelInstructions
  • Resolves templates via await TemplatesRepository.resolve_str(instructions) before passing to GenerativeModel.loop

This pattern is consistent with the Templates subsystem implementation across other generation and stage modules.

src/draive/generation/audio/default.py (1)

8-8: LGTM! Template system migration applied correctly.

The switch from InstructionsRepository.resolve to TemplatesRepository.resolve_str and the broadened instructions parameter type (Template | ModelInstructions) align with the repository-wide template system changes. The resolution path correctly handles both Template objects and plain strings.

Also applies to: 11-11, 19-19, 25-25

src/draive/generation/model/types.py (1)

4-5: LGTM! Protocol signature updated consistently.

The ModelGenerating protocol now accepts Template | ModelInstructions for the instructions parameter, aligning with the broader template system migration across generation modules.

Also applies to: 29-29

src/draive/conversation/realtime/types.py (1)

12-13: LGTM! Realtime conversation protocol updated consistently.

The RealtimeConversationPreparing protocol now accepts Template | ModelInstructions, maintaining consistency with the template system migration across the codebase.

Also applies to: 130-130

src/draive/generation/image/types.py (1)

3-4: LGTM! Image generation protocol updated consistently.

The ImageGenerating protocol signature now accepts Template | ModelInstructions, aligning with the template system migration applied across generation modules.

Also applies to: 15-15

src/draive/multimodal/templates/__init__.py (1)

1-19: LGTM! Public API surface correctly defined.

The template subsystem exports are comprehensive and properly centralized, exposing all essential types (Template, TemplateDeclaration, TemplateMissing) and protocols (TemplateDefining, TemplateListing, TemplateLoading, TemplatesRepository) for the new template system.

Based on learnings.

tests/test_multimodal_template_variables.py (1)

1-207: LGTM! Comprehensive test coverage with past critical issue resolved.

The test suite thoroughly exercises template variable parsing and resolution (text and multimodal), covering edge cases like empty templates, missing arguments, repeated placeholders, metadata boundaries, and malformed markers. All calls to resolve_text_template now correctly use keyword-only arguments= syntax, and the unused-argument test (lines 110-113) correctly verifies that extra arguments are ignored rather than raising errors, matching the implementation behavior.

src/draive/conversation/realtime/state.py (1)

36-46: API update looks consistent.

instructions: Template | ModelInstructions = "" and docstring align with the repo-wide refactor. No behavior regressions spotted.

src/draive/generation/text/state.py (1)

38-47: LGTM — consistent typing and forwarding.

Template | ModelInstructions is applied across overloads; Toolbox.of(...) and example normalization look correct.

src/draive/conversation/completion/types.py (1)

22-33: Align instructions typing across public APIs (allow str or remove str defaults elsewhere).

Here the Protocol requires Template | ModelInstructions, while other modules still accept/ default to str. Decide a single policy:

  • Option A (compat): widen to Template | ModelInstructions | str.
  • Option B (strict): keep as-is and remove str defaults from other APIs.

Confirm direction and apply consistently. As per coding guidelines.

Also applies to: 35-45, 46-55

src/draive/conversation/realtime/default.py (1)

37-45: ****

The review comment is incorrect. ModelInstructions is defined as a type alias for str, not a separate incompatible type. The current code is already correct because:

  • TemplatesRepository.resolve_str accepts Template | str
  • realtime_conversation_preparing receives instructions: Template | ModelInstructions
  • Since ModelInstructions = str, the parameter type is effectively Template | str, which exactly matches what resolve_str accepts
  • No type guard or refactoring is needed

The code at line 52 (and the referenced lines 51–57, which are part of the same function) will work correctly without modification.

Likely an incorrect or invalid review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/draive/conversation/realtime/default.py (1)

51-56: Pass **extra to TemplatesRepository.resolve_str for backend context.

Enables file/postgres backends to use context (e.g., tenant, locale).

-    session_scope: ModelSessionScope = await RealtimeGenerativeModel.session(
-        instructions=await TemplatesRepository.resolve_str(instructions),
+    session_scope: ModelSessionScope = await RealtimeGenerativeModel.session(
+        instructions=await TemplatesRepository.resolve_str(instructions, **extra),
         memory=memory,
         tools=toolbox.available_tools_declaration(),
         output=output,
         **extra,
     )
src/draive/models/generative.py (1)

650-668: Docstring drift: remove InstructionsRepository mention and fix param type.

The method signature is instructions: ModelInstructions = "", but docs say Template | ModelInstructions and reference InstructionsRepository. Update docs for accuracy.

-        Resolves ``instructions`` (via ``InstructionsRepository``), normalizes the provided
-        ``memory`` into a ``ModelMemory`` if a raw context is given, and delegates to the
-        provider's ``session_preparing`` to obtain a ``ModelSessionScope``.
+        Normalizes the provided ``memory`` into a ``ModelMemory`` if a raw context is given,
+        then delegates to the provider's ``session_preparing`` to obtain a ``ModelSessionScope``.
@@
-        instructions : Template | ModelInstructions, optional
-            Instructions or an instruction reference resolvable by ``InstructionsRepository``.
+        instructions : ModelInstructions, optional
+            System/task instructions (string or provider-structured) forwarded to the provider.
src/draive/helpers/instruction_refinement.py (1)

135-158: Preserve repository meta when patching loading.

The wrapped TemplatesRepository drops meta, losing backend/source info. Keep meta=repository.meta.

-        return TemplatesRepository(
-            listing=repository.listing,  # keep current listing
-            loading=instructions_loading,  # replace loading
-            # do not allow modifications - use default noop implementation
-        )
+        return TemplatesRepository(
+            listing=repository.listing,   # keep current listing
+            loading=instructions_loading, # replace loading
+            # intentionally keep default noop `defining`
+            meta=repository.meta,         # preserve source/meta
+        )
♻️ Duplicate comments (16)
src/draive/multimodal/templates/types.py (2)

51-62: Use Map() for consistency and immutability.

Line 60 constructs a plain dict for merged arguments, which is inconsistent with line 43's use of Map() and breaks the immutability pattern expected for State classes.

Apply this diff:

     def with_arguments(
         self,
         **arguments: Multimodal,
     ) -> Self:
         if not arguments:
             return self
 
         return self.__class__(
             identifier=self.identifier,
-            arguments={**self.arguments, **arguments},
+            arguments=Map({**self.arguments, **arguments}),
             meta=self.meta,
         )

As per coding guidelines: use Map for immutable state attributes.


79-96: Prefer Map() for immutability and consistency.

Line 94 initializes variables with a plain dict {}, which is inconsistent with the Map()-based pattern used elsewhere in the codebase for immutable state.

Apply this diff:

         return cls(
             identifier=identifier,
             description=description,
-            variables=variables if variables is not None else {},
+            variables=Map(variables) if variables is not None else Map(),
             meta=Meta.of(meta),
         )

As per coding guidelines: use Map for immutable state attributes.

src/draive/multimodal/templates/variables.py (1)

1-1: Use built-in set[str] per Python 3.12+ guidelines.

The file imports Set from collections.abc and uses Set[str] in type annotations. Per coding guidelines, Python 3.12+ projects should use built-in generics (set[str]) instead.

Apply this diff:

-from collections.abc import Generator, Mapping, MutableSequence, Set
+from collections.abc import Generator, Mapping, MutableSequence

 from haiway import Immutable

 from draive.multimodal.content import Multimodal, MultimodalContent

 __all__ = (
     "parse_template_variables",
     "resolve_multimodal_template",
     "resolve_multimodal_template",
 )


 def parse_template_variables(
     template: str,
-) -> Set[str]:
+) -> set[str]:
     return {
         segment.name
         for segment in _split_template(template)
         if isinstance(segment, TemplateVariable)
     }

As per coding guidelines: use Python 3.12+ built-in generics.

Also applies to: 14-21

docs/guides/Postgres.md (1)

119-120: API naming: use resolve_str, not resolve_as_text.

The documentation references resolve_as_text(template), but the actual TemplatesRepository API exposes resolve_str. Update all references to match the public API.

Apply this diff:

-- `resolve(template)` and `resolve_as_text(template)` reuse a cached loader keyed by identifier to
-  pull the latest template body before rendering arguments.
+- `resolve(template)` and `resolve_str(template)` reuse a cached loader keyed by identifier to pull
+  the latest template body before rendering arguments.
src/draive/postgres/__init__.py (1)

12-12: Export change is correct, but PostgresTemplatesRepository contains a critical SQL syntax error.

The replacement of PostgresInstructionsRepository with PostgresTemplatesRepository aligns with the template system migration. However, the exported PostgresTemplatesRepository implementation contains a critical SQL syntax error in src/draive/postgres/templates.py (lines 137-145) that will cause runtime failures when define() is called—a missing comma between $4::JSONB and $5::JSONB in the INSERT VALUES clause.

Also applies to: 23-23

src/draive/postgres/templates.py (1)

128-166: LGTM — Define implementation with cache invalidation.

The SQL INSERT properly parameterizes all values, and cache clearing (lines 164-165) ensures consistency after defining new templates. The past review comment about a missing comma between $4::JSONB and $5::JSONB has been addressed — line 153 shows the comma is present.

src/draive/helpers/instruction_preparation.py (1)

26-44: LGTM — Function signature and instantiation updated for TemplateDeclaration.

The parameter type change and instantiation logic correctly migrate from InstructionsDeclaration to TemplateDeclaration. The past review comment about preferring TemplateDeclaration.of(...) over direct construction (lines 37-40) remains a valid refinement for consistency with State constructor patterns.

src/draive/conversation/completion/state.py (1)

37-43: Type default mismatch: add | str to instructions (3 sites).

Defaults are "" but the annotation excludes str. Align types across overloads and impl.

-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",
@@
-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",
@@
-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",

Also applies to: 50-56, 62-68

src/draive/generation/model/state.py (1)

23-30: Type default mismatch: add | str to instructions (3 sites).

Defaults use ""; include str to match runtime and prior API.

-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",
@@
-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",
@@
-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",

Also applies to: 38-45, 53-60

src/draive/generation/model/default.py (1)

36-46: Unify resolution via TemplatesRepository and pass through **extra.

Avoid str.format(...) and keep a single resolution path so string/Template behave identically and repository backends can use extra context. Also reuse computed schema variable. This mirrors the earlier guidance.

@@
-            case "full":
-                if isinstance(instructions, str):
-                    resolved_instructions = instructions.format(
-                        model_schema=generated.json_schema(indent=2),
-                    )
-                else:
-                    resolved_instructions = await TemplatesRepository.resolve_str(
-                        instructions.with_arguments(
-                            model_schema=generated.json_schema(indent=2),
-                        )
-                    )
+            case "full":
+                schema = generated.json_schema(indent=2)
+                if isinstance(instructions, str):
+                    # Resolve via repository for consistent placeholder semantics
+                    resolved_instructions = await TemplatesRepository.resolve_str(
+                        Template.of("__inline__", arguments={"model_schema": schema}),
+                        default=instructions,
+                        **extra,
+                    )
+                else:
+                    resolved_instructions = await TemplatesRepository.resolve_str(
+                        instructions.with_arguments(model_schema=schema),
+                        **extra,
+                    )
@@
-            case "simplified":
-                if isinstance(instructions, str):
-                    resolved_instructions = instructions.format(
-                        model_schema=generated.simplified_schema(indent=2),
-                    )
-                else:
-                    resolved_instructions = await TemplatesRepository.resolve_str(
-                        instructions.with_arguments(
-                            model_schema=generated.simplified_schema(indent=2),
-                        )
-                    )
+            case "simplified":
+                schema = generated.simplified_schema(indent=2)
+                if isinstance(instructions, str):
+                    resolved_instructions = await TemplatesRepository.resolve_str(
+                        Template.of("__inline__", arguments={"model_schema": schema}),
+                        default=instructions,
+                        **extra,
+                    )
+                else:
+                    resolved_instructions = await TemplatesRepository.resolve_str(
+                        instructions.with_arguments(model_schema=schema),
+                        **extra,
+                    )
@@
-            case "skip":  # instruction is not modified
-                resolved_instructions = await TemplatesRepository.resolve_str(instructions)
+            case "skip":  # instruction is not modified
+                resolved_instructions = await TemplatesRepository.resolve_str(
+                    instructions,
+                    **extra,
+                )

Also applies to: 49-59, 61-63

src/draive/__init__.py (1)

290-293: Remove duplicate entry in all.

ModelInstructions appears twice, which is noisy and can confuse tooling.

     "ModelInputInvalid",
-    "ModelInstructions",
-    "ModelInstructions",
+    "ModelInstructions",
     "ModelMemory",
src/draive/conversation/completion/default.py (1)

18-20: Import core types from the public API.

Use draive.models instead of the internal draive.models.types.

-from draive.models.types import ModelInstructions, ModelToolRequest
+from draive.models import ModelInstructions, ModelToolRequest
src/draive/multimodal/templates/repository.py (4)

313-318: Return templates in a stable order.

Dict iteration order isn’t guaranteed across processes; sort by identifier to avoid flakiness.

-        return tuple(self._declarations.values())
+        return tuple(sorted(self._declarations.values(), key=lambda d: d.identifier))
-        return tuple(self._declarations.values())
+        return tuple(sorted(self._declarations.values(), key=lambda d: d.identifier))

Also applies to: 391-393


487-498: Write templates deterministically for reproducible files.

Sort declarations and JSON keys to keep file content stable across writes.

-        file_contents: bytes = json.dumps(
-            [
-                {
-                    "identifier": declaration.identifier,
-                    "description": declaration.description,
-                    "variables": declaration.variables,
-                    "content": self._contents[declaration.identifier],
-                    "meta": declaration.meta.to_mapping(),
-                }
-                for declaration in self._declarations.values()
-            ]
-        ).encode()
+        ordered = sorted(self._declarations.values(), key=lambda d: d.identifier)
+        file_contents: bytes = json.dumps(
+            [
+                {
+                    "identifier": d.identifier,
+                    "description": d.description,
+                    "variables": d.variables,
+                    "content": self._contents[d.identifier],
+                    "meta": d.meta.to_mapping(),
+                }
+                for d in ordered
+            ],
+            sort_keys=True,
+        ).encode()

60-88: Add concise NumPy‑style docstrings to public API.

Public class/methods lack docstrings (Parameters/Returns/Raises). Please add them for TemplatesRepository and its public methods.

I can draft minimal docstrings aligned with current behavior if helpful.

Also applies to: 103-121, 146-182, 206-245, 246-263, 285-301


458-460: Use ctx.log_warn, not ctx.log_warning (runtime error risk).

ctx.log_warning likely doesn’t exist; this will raise at runtime. Use ctx.log_warn.

-                                ctx.log_warning(
+                                ctx.log_warn(
                                     "Invalid templates file storage element, skipping..."
                                 )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 05f22c5 and 1719705.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (41)
  • Makefile (1 hunks)
  • docs/guides/Postgres.md (2 hunks)
  • pyproject.toml (1 hunks)
  • src/draive/__init__.py (3 hunks)
  • src/draive/conversation/completion/default.py (7 hunks)
  • src/draive/conversation/completion/state.py (5 hunks)
  • src/draive/conversation/completion/types.py (4 hunks)
  • src/draive/conversation/realtime/default.py (3 hunks)
  • src/draive/conversation/realtime/state.py (3 hunks)
  • src/draive/conversation/realtime/types.py (2 hunks)
  • src/draive/generation/audio/default.py (1 hunks)
  • src/draive/generation/audio/state.py (4 hunks)
  • src/draive/generation/audio/types.py (2 hunks)
  • src/draive/generation/image/default.py (1 hunks)
  • src/draive/generation/image/state.py (4 hunks)
  • src/draive/generation/image/types.py (2 hunks)
  • src/draive/generation/model/default.py (3 hunks)
  • src/draive/generation/model/state.py (4 hunks)
  • src/draive/generation/model/types.py (2 hunks)
  • src/draive/generation/text/default.py (1 hunks)
  • src/draive/generation/text/state.py (4 hunks)
  • src/draive/generation/text/types.py (2 hunks)
  • src/draive/helpers/instruction_preparation.py (3 hunks)
  • src/draive/helpers/instruction_refinement.py (5 hunks)
  • src/draive/models/__init__.py (0 hunks)
  • src/draive/models/generative.py (5 hunks)
  • src/draive/models/instructions/__init__.py (0 hunks)
  • src/draive/models/instructions/repository.py (0 hunks)
  • src/draive/models/instructions/template.py (0 hunks)
  • src/draive/models/instructions/types.py (0 hunks)
  • src/draive/multimodal/__init__.py (2 hunks)
  • src/draive/multimodal/templates/__init__.py (1 hunks)
  • src/draive/multimodal/templates/repository.py (1 hunks)
  • src/draive/multimodal/templates/types.py (1 hunks)
  • src/draive/multimodal/templates/variables.py (1 hunks)
  • src/draive/openai/images.py (3 hunks)
  • src/draive/postgres/__init__.py (2 hunks)
  • src/draive/postgres/instructions.py (0 hunks)
  • src/draive/postgres/templates.py (1 hunks)
  • src/draive/stages/stage.py (15 hunks)
  • tests/test_multimodal_template_variables.py (1 hunks)
💤 Files with no reviewable changes (6)
  • src/draive/models/instructions/template.py
  • src/draive/postgres/instructions.py
  • src/draive/models/instructions/init.py
  • src/draive/models/instructions/types.py
  • src/draive/models/instructions/repository.py
  • src/draive/models/init.py
🧰 Additional context used
📓 Path-based instructions (12)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use Python 3.12+ features and syntax across the codebase
Format code exclusively with Ruff (make format); do not use other formatters
Skip module-level docstrings

Files:

  • src/draive/stages/stage.py
  • src/draive/multimodal/templates/types.py
  • src/draive/multimodal/templates/variables.py
  • src/draive/generation/model/types.py
  • src/draive/generation/image/state.py
  • src/draive/generation/audio/default.py
  • src/draive/models/generative.py
  • src/draive/generation/audio/types.py
  • src/draive/conversation/realtime/state.py
  • src/draive/__init__.py
  • src/draive/conversation/realtime/types.py
  • src/draive/generation/text/types.py
  • src/draive/postgres/templates.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/generation/audio/state.py
  • src/draive/postgres/__init__.py
  • src/draive/generation/model/state.py
  • src/draive/conversation/completion/state.py
  • src/draive/generation/model/default.py
  • src/draive/conversation/completion/types.py
  • src/draive/generation/text/state.py
  • src/draive/generation/text/default.py
  • src/draive/openai/images.py
  • tests/test_multimodal_template_variables.py
  • src/draive/generation/image/types.py
  • src/draive/conversation/completion/default.py
  • src/draive/generation/image/default.py
  • src/draive/conversation/realtime/default.py
  • src/draive/multimodal/__init__.py
  • src/draive/multimodal/templates/repository.py
  • src/draive/helpers/instruction_refinement.py
  • src/draive/helpers/instruction_preparation.py
src/draive/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/**/*.py: Import Haiway symbols directly (from haiway import State, ctx)
Use ctx.scope(...) to bind scoped Disposables and active State; avoid global state
Route all logs through ctx.log_debug/info/warn/error; do not use print
Use latest, most strict typing syntax (Python 3.12+), with strict typing only for public APIs
Avoid loose Any except at explicit third‑party boundaries
Prefer explicit attribute access with static types; avoid dynamic getattr except at narrow boundaries
Prefer Mapping/Sequence/Iterable in public types over dict/list/set
Use final where applicable; avoid inheritance and prefer composition
Use precise unions (|) and narrow with match/isinstance; avoid cast unless provably safe and localized
Model immutable data/config and facades with haiway.State; provide ergonomic classmethods like .of(...)
Avoid in-place mutation; use State.updated(...) or functional builders to produce new instances
Access active state via haiway.ctx inside async scopes (ctx.scope(...))
Use @statemethod for public state methods that dispatch on the active instance
Log around generation calls, tool dispatch, and provider requests/responses without leaking secrets; prefer structured/concise messages
Add metrics via ctx.record where applicable
All I/O is async; keep boundaries async and use ctx.spawn for detached tasks
Use structured concurrency and valid coroutine usage; rely on haiway/asyncio; avoid custom threading
Construct multimodal content with MultimodalContent.of(...) and compose blocks explicitly
Use ResourceContent/ResourceReference for media/data blobs
Wrap custom types/data within ArtifactContent; use hidden when needed
Add NumPy-style docstrings for public symbols with Parameters/Returns/Raises and rationale when non-obvious
Avoid docstrings on internal helpers; keep names self-explanatory
Keep docstrings high-quality; mkdocstrings pulls them into API reference
Never log secrets or full request bodies containing keys/tokens

Files:

  • src/draive/stages/stage.py
  • src/draive/multimodal/templates/types.py
  • src/draive/multimodal/templates/variables.py
  • src/draive/generation/model/types.py
  • src/draive/generation/image/state.py
  • src/draive/generation/audio/default.py
  • src/draive/models/generative.py
  • src/draive/generation/audio/types.py
  • src/draive/conversation/realtime/state.py
  • src/draive/__init__.py
  • src/draive/conversation/realtime/types.py
  • src/draive/generation/text/types.py
  • src/draive/postgres/templates.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/generation/audio/state.py
  • src/draive/postgres/__init__.py
  • src/draive/generation/model/state.py
  • src/draive/conversation/completion/state.py
  • src/draive/generation/model/default.py
  • src/draive/conversation/completion/types.py
  • src/draive/generation/text/state.py
  • src/draive/generation/text/default.py
  • src/draive/openai/images.py
  • src/draive/generation/image/types.py
  • src/draive/conversation/completion/default.py
  • src/draive/generation/image/default.py
  • src/draive/conversation/realtime/default.py
  • src/draive/multimodal/__init__.py
  • src/draive/multimodal/templates/repository.py
  • src/draive/helpers/instruction_refinement.py
  • src/draive/helpers/instruction_preparation.py
src/draive/stages/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement pipeline stage abstractions and helpers under draive/stages/

Files:

  • src/draive/stages/stage.py
src/draive/multimodal/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement content types and helpers (MultimodalContent, TextContent, ArtifactContent) under draive/multimodal/

Files:

  • src/draive/multimodal/templates/types.py
  • src/draive/multimodal/templates/variables.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/multimodal/__init__.py
  • src/draive/multimodal/templates/repository.py
src/draive/models/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Keep core abstractions (GenerativeModel, tools, instructions) under draive/models/

Files:

  • src/draive/models/generative.py
src/draive/conversation/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement higher-level chat/realtime conversations under draive/conversation/

Files:

  • src/draive/conversation/realtime/state.py
  • src/draive/conversation/realtime/types.py
  • src/draive/conversation/completion/state.py
  • src/draive/conversation/completion/types.py
  • src/draive/conversation/completion/default.py
  • src/draive/conversation/realtime/default.py
docs/**/*

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*: When behavior/API changes, update relevant docs under docs/ and examples as applicable
When adding public APIs, update examples/guides and ensure cross-links render

Files:

  • docs/guides/Postgres.md
src/draive/__init__.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/__init__.py: Centralize public exports in src/draive/init.py
Update src/draive/init.py exports when API surface changes

Files:

  • src/draive/__init__.py
src/draive/{httpx,mcp,postgres,opentelemetry}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Place integrations under draive/httpx, draive/mcp, draive/postgres, draive/opentelemetry

Files:

  • src/draive/postgres/templates.py
  • src/draive/postgres/__init__.py
{pyproject.toml,pyrightconfig.json}

📄 CodeRabbit inference engine (AGENTS.md)

Use Ruff, Bandit, and Pyright (strict) via make lint

Files:

  • pyproject.toml
src/draive/{openai,anthropic,mistral,gemini,vllm,ollama,bedrock,cohere}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/{openai,anthropic,mistral,gemini,vllm,ollama,bedrock,cohere}/**/*.py: Provider-specific feature modules live under their respective provider directories
Translate provider/SDK errors into typed exceptions; do not raise bare Exception and preserve context
Use environment variables for credentials and resolve via helper functions like getenv_str

Files:

  • src/draive/openai/images.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.py: Do not perform real network I/O in unit tests; mock providers/HTTP
Keep tests fast and focused on changed code; start with unit tests around new types/functions/adapters
Use fixtures from tests/ or add focused ones; avoid heavy integration scaffolding
Use pytest-asyncio for coroutine tests (@pytest.mark.asyncio)
Prefer scoping with ctx.scope(...) in async tests and bind required State instances explicitly
Avoid real I/O and network in async tests; stub provider calls and HTTP

Files:

  • tests/test_multimodal_template_variables.py
🧠 Learnings (6)
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/models/**/*.py : Keep core abstractions (GenerativeModel, tools, instructions) under draive/models/

Applied to files:

  • src/draive/stages/stage.py
  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/**/*.py : Add NumPy-style docstrings for public symbols with Parameters/Returns/Raises and rationale when non-obvious

Applied to files:

  • src/draive/multimodal/templates/types.py
  • src/draive/multimodal/templates/repository.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/__init__.py : Centralize public exports in src/draive/__init__.py

Applied to files:

  • src/draive/__init__.py
  • src/draive/multimodal/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/__init__.py : Update src/draive/__init__.py exports when API surface changes

Applied to files:

  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/multimodal/**/*.py : Implement content types and helpers (MultimodalContent, TextContent, ArtifactContent) under draive/multimodal/

Applied to files:

  • src/draive/__init__.py
  • src/draive/multimodal/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/**/*.py : Import Haiway symbols directly (from haiway import State, ctx)

Applied to files:

  • src/draive/helpers/instruction_preparation.py
🧬 Code graph analysis (31)
src/draive/stages/stage.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-306)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/multimodal/templates/types.py (1)
src/draive/stages/stage.py (1)
  • with_meta (1390-1435)
src/draive/multimodal/templates/variables.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (2)
  • of (33-45)
  • of (82-96)
src/draive/generation/model/types.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/image/state.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/audio/default.py (3)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/models/generative.py (5)
  • completion (62-71)
  • completion (74-83)
  • completion (87-96)
  • completion (99-108)
  • completion (111-181)
src/draive/generation/audio/types.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/conversation/realtime/state.py (3)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/__init__.py (2)
src/draive/multimodal/templates/types.py (3)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateMissing (18-27)
src/draive/multimodal/templates/repository.py (1)
  • TemplatesRepository (61-306)
src/draive/conversation/realtime/types.py (2)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/text/types.py (3)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/postgres/templates.py (3)
src/draive/multimodal/templates/repository.py (11)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
  • TemplatesRepository (61-306)
  • listing (313-317)
  • listing (384-392)
  • load (247-262)
  • loading (319-325)
  • loading (394-404)
  • defining (327-342)
  • defining (406-425)
src/draive/multimodal/templates/types.py (1)
  • TemplateDeclaration (80-101)
src/draive/parameters/model.py (2)
  • from_json (454-469)
  • to_json (501-517)
src/draive/multimodal/templates/__init__.py (2)
src/draive/multimodal/templates/repository.py (4)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
  • TemplatesRepository (61-306)
src/draive/multimodal/templates/types.py (6)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateDefining (123-132)
  • TemplateListing (105-109)
  • TemplateLoading (113-119)
  • TemplateMissing (18-27)
src/draive/generation/audio/state.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/postgres/__init__.py (2)
src/draive/multimodal/templates/repository.py (3)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
src/draive/postgres/templates.py (1)
  • PostgresTemplatesRepository (28-173)
src/draive/generation/model/state.py (4)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/conversation/completion/state.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/model/default.py (4)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (2)
  • Template (31-76)
  • with_arguments (51-62)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/parameters/model.py (2)
  • json_schema (443-451)
  • simplified_schema (433-440)
src/draive/conversation/completion/types.py (2)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/text/state.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/text/default.py (3)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/models/generative.py (5)
  • loop (185-194)
  • loop (197-206)
  • loop (210-219)
  • loop (222-231)
  • loop (234-306)
src/draive/openai/images.py (3)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-306)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
tests/test_multimodal_template_variables.py (3)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/variables.py (3)
  • parse_template_variables (14-21)
  • resolve_multimodal_template (48-67)
  • resolve_text_template (24-45)
src/draive/multimodal/text.py (1)
  • TextContent (11-82)
src/draive/generation/image/types.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/conversation/completion/default.py (3)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-306)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/models/generative.py (5)
  • loop (185-194)
  • loop (197-206)
  • loop (210-219)
  • loop (222-231)
  • loop (234-306)
src/draive/generation/image/default.py (4)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-306)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/openai/images.py (1)
  • generate_image (21-60)
src/draive/conversation/completion/state.py (3)
  • completion (34-43)
  • completion (47-56)
  • completion (59-136)
src/draive/conversation/realtime/default.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/multimodal/__init__.py (2)
src/draive/multimodal/templates/repository.py (4)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
  • TemplatesRepository (61-306)
src/draive/multimodal/templates/types.py (6)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateDefining (123-132)
  • TemplateListing (105-109)
  • TemplateLoading (113-119)
  • TemplateMissing (18-27)
src/draive/multimodal/templates/repository.py (3)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (8)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateDefining (123-132)
  • TemplateListing (105-109)
  • TemplateLoading (113-119)
  • TemplateMissing (18-27)
  • of (33-45)
  • of (82-96)
src/draive/multimodal/templates/variables.py (3)
  • parse_template_variables (14-21)
  • resolve_multimodal_template (48-67)
  • resolve_text_template (24-45)
src/draive/helpers/instruction_refinement.py (5)
src/draive/multimodal/content.py (2)
  • MultimodalContent (25-592)
  • MultimodalTag (599-700)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-306)
  • loading (319-325)
  • loading (394-404)
  • load (247-262)
src/draive/multimodal/text.py (1)
  • TextContent (11-82)
src/draive/postgres/templates.py (2)
  • loading (121-126)
  • load (88-119)
src/draive/helpers/instruction_preparation.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • TemplateDeclaration (80-101)
🔇 Additional comments (49)
src/draive/multimodal/templates/types.py (1)

1-15: LGTM!

The imports are clean and the __all__ exports now correctly include all public types including protocols and declarations.

src/draive/multimodal/templates/variables.py (2)

7-11: Verify __all__ fix was applied.

The __all__ tuple appears to still have a duplicate "resolve_multimodal_template" entry and is missing "resolve_text_template". Past review comment indicated this was addressed in commit 05f22c5, but the code still shows the issue.

Please confirm whether the fix from commit 05f22c5 was applied. The correct __all__ should be:

__all__ = (
    "parse_template_variables",
    "resolve_text_template",
    "resolve_multimodal_template",
)

70-112: LGTM!

The template parsing logic correctly handles edge cases including missing closing markers, whitespace validation, and proper cursor advancement. The {% variable %} syntax is clearly defined and robustly implemented.

pyproject.toml (1)

8-8: LGTM!

Version bump to 0.90.0 is appropriate for this major feature addition (Templates subsystem). The semver increment reflects the significant API surface changes documented in the PR.

Makefile (2)

10-10: Verify intentional removal of .env loading.

The -include .env directive was removed (or commented out). This might affect local development workflows that rely on environment variables from .env files.

Please confirm this removal is intentional and document if developers need to source .env manually or if environment configuration has moved elsewhere.


13-13: LGTM!

UV version bump from 0.9.0 to 0.9.4 brings in patch-level improvements and bug fixes.

src/draive/stages/stage.py (6)

49-49: LGTM!

Import correctly adds Template and TemplatesRepository from the new multimodal templates subsystem.


288-372: LGTM!

The completion method correctly migrated to accept Template | ModelInstructions and resolves instructions via await TemplatesRepository.resolve_str(instructions). The resolution is properly awaited and the result is passed to GenerativeModel.loop.


374-463: LGTM!

The prompting_completion method follows the same consistent pattern as completion, correctly handling Template | ModelInstructions resolution.


465-553: LGTM!

The loopback_completion method correctly implements template resolution following the established pattern.


555-622: LGTM!

The result_completion method completes the consistent migration pattern across all completion methods.


2045-2093: LGTM!

The _model_routing helper correctly constructs routing instructions and resolves them via TemplatesRepository.resolve_str. The method properly handles the case where instructions is a plain string (resolve_str returns it as-is per the implementation).

src/draive/multimodal/__init__.py (1)

8-16: LGTM!

Template-related symbols are correctly imported from draive.multimodal.templates and exported via __all__. This properly expands the public API surface for the new Templates subsystem.

Based on learnings: centralizing public exports in package __init__.py files.

Also applies to: 25-31

src/draive/generation/audio/default.py (2)

8-11: LGTM!

Imports correctly updated to include ModelInstructions from models and Template, TemplatesRepository from multimodal, replacing the old Instructions subsystem.


17-29: LGTM!

The generate_audio function correctly migrated to accept Template | ModelInstructions and resolves instructions via await TemplatesRepository.resolve_str(instructions) before passing to GenerativeModel.completion. The async resolution is properly awaited.

src/draive/generation/audio/types.py (1)

3-4: LGTM!

The AudioGenerating protocol signature correctly updated to accept Template | ModelInstructions, maintaining consistency with the implementation in default.py and the broader Templates subsystem migration.

Also applies to: 15-15

src/draive/generation/model/types.py (2)

4-5: LGTM: Import updates align with template-based refactor.

The imports correctly reflect the transition from ResolveableInstructions to Template and ModelInstructions, consistent with the broader subsystem migration.


29-29: LGTM: Type signature broadened to support Template | ModelInstructions.

The updated parameter type expands the acceptable instruction representations while maintaining backward compatibility with ModelInstructions.

src/draive/conversation/realtime/types.py (2)

12-13: LGTM: Import updates support template-based instructions.

The imports correctly add Template and ModelInstructions to support the refactored instruction handling.


130-130: LGTM: Protocol signature updated to accept Template | ModelInstructions.

The parameter type change aligns with the subsystem migration and maintains consistency across realtime conversation interfaces.

src/draive/generation/image/types.py (2)

3-4: LGTM: Imports updated for template-based instructions.

The import changes correctly reflect the migration from ResolveableInstructions to Template and ModelInstructions.


15-15: LGTM: ImageGenerating protocol accepts Template | ModelInstructions.

The broadened parameter type maintains consistency with the template subsystem refactor.

src/draive/multimodal/templates/__init__.py (1)

1-19: LGTM: Clean public API exposure for the templates subsystem.

The module correctly exports all essential Template-related types and the TemplatesRepository, following standard Python packaging conventions.

src/draive/conversation/realtime/state.py (2)

14-14: LGTM: Imports updated to support Template | ModelInstructions.

The import additions correctly support the refactored instruction parameter type.

Also applies to: 22-22


40-40: LGTM: Parameter type and docstring updated consistently.

The instructions parameter type and documentation accurately reflect the Template | ModelInstructions union, maintaining consistency with the broader refactor.

Also applies to: 50-50

tests/test_multimodal_template_variables.py (3)

12-86: LGTM: Comprehensive variable parsing tests.

The test suite thoroughly covers:

  • Unique variable extraction with deduplication
  • Edge cases: empty templates, unmatched/unclosed delimiters, whitespace/padding, empty names
  • Adjacent placeholders and symbolic names with hyphens
  • Literal brace handling

88-127: LGTM: Text template resolution tests are thorough.

The tests correctly verify:

  • Placeholder substitution and repetition
  • Missing argument detection with specific KeyError message
  • Unused arguments are silently ignored (correct behavior per implementation)
  • Edge-position placeholders and empty template handling
  • All calls use correct keyword argument syntax (arguments=arguments)

129-207: LGTM: Multimodal template resolution tests are excellent.

The test coverage validates:

  • String and TextContent argument handling
  • Proper MultimodalContent construction and flattening
  • Missing/unused argument behavior consistent with text templates
  • Nested content flattening
  • Metadata boundary preservation (lines 180-201 provide particularly strong validation)
  • Empty template returning MultimodalContent.empty

All calls correctly use keyword argument syntax.

src/draive/generation/text/default.py (2)

9-13: LGTM — Imports align with Template subsystem migration.

The updated imports correctly bring in ModelInstructions, Template, and TemplatesRepository to support the new template-based instruction flow.


20-28: LGTM — Parameter type and resolution logic updated consistently.

The signature now accepts Template | ModelInstructions, and the resolution correctly uses TemplatesRepository.resolve_str(instructions) to produce the final instruction string. The flow is consistent with the broader Template subsystem adoption.

src/draive/generation/image/default.py (2)

8-11: LGTM — Imports updated for Template subsystem.

Correctly imports ModelInstructions, Template, and TemplatesRepository consistent with the migration.


19-25: LGTM — Signature and resolution align with Template subsystem.

The parameter type change and resolution call match the pattern used in other generation modules.

src/draive/generation/image/state.py (2)

7-9: LGTM — Imports updated for Template support.

Correct imports for ModelInstructions and Template.


20-38: LGTM — Overloads and implementation signature consistent.

All three signatures (classmethod overload, instance overload, and implementation) now accept Template | ModelInstructions, maintaining type consistency throughout the public API surface.

src/draive/generation/text/types.py (2)

4-5: LGTM — Imports updated for Template subsystem.

Correctly imports ModelInstructions and Template.


15-20: LGTM — Protocol signature updated consistently.

The __call__ signature now accepts Template | ModelInstructions for instructions and includes **extra: Any for provider-specific parameters, matching the implementation pattern across generation modules.

src/draive/postgres/templates.py (4)

28-45: LGTM — Well-documented factory with clear parameters.

The docstring follows NumPy style and clearly documents the caching parameters and return type.


47-82: LGTM — Listing implementation with appropriate caching.

The query uses DISTINCT ON (identifier) with ORDER BY created DESC to fetch the latest version of each template. Caching (limit=1, expiration=600s) is reasonable for listing operations. JSON parsing for variables and Meta includes safe fallbacks.


84-126: LGTM — Load implementation with proper caching strategy.

The load function caches individual templates (limit=32) and logs status appropriately. The loading wrapper delegates to load, maintaining the expected interface.


168-173: LGTM — Repository construction wired correctly.

The factory returns a properly configured TemplatesRepository with all required callbacks and metadata.

src/draive/openai/images.py (2)

8-9: LGTM — Imports updated for Template subsystem.

Correctly imports ModelInstructions, Template, and TemplatesRepository.


24-36: LGTM — OpenAI image generation aligned with Template flow.

The parameter type and resolution call match the pattern used across generation modules. The prompt is correctly resolved via TemplatesRepository.resolve_str(instructions) before passing to the OpenAI API.

src/draive/generation/text/state.py (2)

8-9: LGTM — Imports updated for Template subsystem.

Correctly imports ModelInstructions and Template.


20-42: LGTM — Signatures updated consistently across overloads.

All three signatures (classmethod overload at line 20, instance overload at line 31, and implementation at line 42) now accept Template | ModelInstructions, ensuring type consistency throughout the public API.

src/draive/helpers/instruction_preparation.py (2)

5-6: LGTM — Imports updated for Template subsystem.

Correctly imports ModelInstructions and TemplateDeclaration.


68-77: LGTM — Variable formatting updated for TemplateDeclaration.

The function now correctly operates on TemplateDeclaration.variables instead of arguments, maintaining the same formatting logic.

src/draive/conversation/completion/types.py (1)

8-10: LGTM — protocol types aligned with Template/ModelInstructions.

Signatures look consistent; no default/typing mismatch here.

Also applies to: 26-55

src/draive/__init__.py (1)

172-183: Top‑level Template exports look correct.

Re-exporting Template, TemplateDeclaration, TemplateMissing, and TemplatesRepository from draive.multimodal and listing them in __all__ is consistent with the centralized public API strategy.

Also applies to: 351-354

src/draive/conversation/completion/default.py (1)

81-107: Template-based resolution and loop wiring LGTM.

The switch to Template | ModelInstructions and TemplatesRepository.resolve_str(...) is clean, and pre-resolving in non‑streaming path is appropriate.

Please ensure all other call sites use the same Template | ModelInstructions union and do not rely on removed Instructions APIs.

Also applies to: 134-141

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (10)
src/draive/stages/stage.py (1)

794-801: tool_call API contradicts itself: exposes *args but forbids them.

Signature advertises positional args, docstring documents them, yet runtime asserts they’re not supported. Remove *args (or support them end‑to‑end).

Minimal fix (remove *args and update docs):

-    def tool_call[**Args, Result](
+    def tool_call[**Args, Result](
         cls,
         _tool: FunctionTool[Args, Result],
         /,
-        *args: Args.args,
         **kwargs: Args.kwargs,
     ) -> Self:
@@
-        *args : Args.args
-            Positional arguments to pass to the tool.
         **kwargs : Args.kwargs
             Keyword arguments to pass to the tool.
@@
-        assert not args, "Positional arguments are not supported"  # nosec: B101
+        # Only keyword arguments are supported for tool calls

If you prefer supporting positionals, forward them to _tool.call(request_id, *args, **kwargs) and drop the assert.

Also applies to: 813-826, 835-835

Makefile (1)

43-44: Wording nit: make version check message accurate.

Current message says “up‑to‑date” even when current > minimum. Suggest:

-			echo '...uv version is up-to-date!'; \
+			echo '...uv meets minimum version $(UV_VERSION)!'; \
src/draive/openai/images.py (1)

33-41: Thread input into template resolution for prompt composition.

Allow templates to reference the input by passing it as an argument.

-                prompt=await TemplatesRepository.resolve_str(instructions),
+                prompt=await TemplatesRepository.resolve_str(
+                    instructions,
+                    arguments={"input": input},
+                ),
src/draive/generation/text/types.py (1)

10-20: Add a short API docstring to TextGenerating.

Public protocols should be documented for mkdocstrings.

 @runtime_checkable
 class TextGenerating(Protocol):
+    """Protocol for text generation callables.
+
+    Parameters
+    ----------
+    instructions : Template | ModelInstructions
+        Templated system instruction or prepared instruction string.
+    input : MultimodalContent
+        Normalized input content.
+    toolbox : Toolbox
+        Tools available for function calling.
+    examples : Iterable[tuple[MultimodalContent, str]]
+        Few-shot examples as (input, expected_output).
+    **extra : Any
+        Provider- or pipeline-specific extras.
+
+    Returns
+    -------
+    str
+        Generated text.
+    """
     async def __call__(
src/draive/generation/text/state.py (2)

14-14: Document the TextGeneration state.

Add a concise class docstring for API reference.

 class TextGeneration(State):
+    """Facade for text generation.
+
+    Use `TextGeneration.generate(...)` to invoke the active generator.
+    """

38-47: Docstring for generate to clarify parameters and defaults.

Document parameters, defaults, and return type.

     @statemethod
     async def generate(
         self,
         *,
         instructions: Template | ModelInstructions = "",
         input: Multimodal,  # noqa: A002
         tools: Toolbox | Iterable[Tool] = (),
         examples: Iterable[tuple[Multimodal, str]] = (),
         **extra: Any,
     ) -> str:
+        """Generate text using the active generator.
+
+        Parameters
+        ----------
+        instructions : Template | ModelInstructions, default ""
+            System instruction template or prepared string.
+        input : Multimodal
+            Input content (normalized to MultimodalContent).
+        tools : Toolbox | Iterable[Tool], default ()
+            Tools available for function calling.
+        examples : Iterable[tuple[Multimodal, str]], default ()
+            Few-shot examples as (input, expected_output).
+        **extra : Any
+            Provider/pipeline-specific extras.
+
+        Returns
+        -------
+        str
+            Generated text.
+        """
src/draive/generation/image/state.py (1)

20-41: Type-default mismatch: add | str to the union.

The instructions parameter has a default value of "" (empty string), but the type annotation Template | ModelInstructions does not include str. This creates a type inconsistency unless ModelInstructions is an alias for str.

Based on the past review comment on src/draive/generation/audio/state.py (lines 20-23), this is a known issue. Add | str to all three signatures to match the default value.

Apply this diff:

-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",
@@
-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",
@@
-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",
src/draive/generation/model/state.py (1)

23-60: Type-default mismatch: add | str to the union.

The instructions parameter has a default value of "" but the type annotation Template | ModelInstructions does not include str. Add | str to all three signatures for consistency with the default value.

Apply this diff:

-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",
@@
-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",
@@
-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",
src/draive/conversation/completion/state.py (1)

37-73: Type-default mismatch: add | str to the union.

The instructions parameter has a default value of "" but the type annotation Template | ModelInstructions does not include str. Add | str to all three signatures and update the docstring on line 73 accordingly.

Apply this diff:

-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",
@@
-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",
@@
-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",
@@
-        instructions : Template | ModelInstructions, optional
+        instructions : Template | ModelInstructions | str, optional
src/draive/models/generative.py (1)

642-657: Resolve signature-documentation mismatch.

The signature on line 642 declares instructions: ModelInstructions = "" but the documentation on line 656 states Template | ModelInstructions. This inconsistency should be resolved.

If the signature should accept both types (consistent with other generation methods in the codebase), update the signature:

-        instructions: ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",

Otherwise, update the documentation to match the signature:

-        instructions : Template | ModelInstructions, optional
+        instructions : ModelInstructions, optional

Note: If accepting the union type, you may need to add resolution logic before passing to session_preparing (see line 694).

♻️ Duplicate comments (18)
src/draive/multimodal/templates/variables.py (2)

7-11: Fix __all__: add resolve_text_template, remove duplicate.

Export the intended public API.

 __all__ = (
     "parse_template_variables",
-    "resolve_multimodal_template",
-    "resolve_multimodal_template",
+    "resolve_text_template",
+    "resolve_multimodal_template",
 )

1-1: Use built‑in generics (set[str]) and drop Set import (Python 3.12).

Modernize annotations per guidelines.

-from collections.abc import Generator, Mapping, MutableSequence, Set
+from collections.abc import Generator, Mapping, MutableSequence
@@
-def parse_template_variables(
-    template: str,
-) -> Set[str]:
+def parse_template_variables(
+    template: str,
+) -> set[str]:

As per coding guidelines.

Also applies to: 14-16

src/draive/multimodal/templates/types.py (5)

91-96: Use Map() for TemplateDeclaration.variables by default.

Align with immutable Map pattern; wrap provided mappings as needed.

     ) -> Self:
         return cls(
             identifier=identifier,
             description=description,
-            variables=variables if variables is not None else {},
+            variables=Map(variables) if variables is not None else Map(),
             meta=Meta.of(meta),
         )

18-28: Add docstring for public exception TemplateMissing.

Brief NumPy‑style docstring improves API clarity.

 class TemplateMissing(Exception):
+    """
+    Raised when a requested template cannot be found.
+
+    Parameters
+    ----------
+    identifier : str
+        Identifier of the missing template.
+    """
     __slots__ = ("identifier",)

79-97: Add NumPy‑style docstring to TemplateDeclaration.

Describe attributes and usage (identifier, description, variables, meta).


58-62: Preserve immutability: use Map() when merging arguments.

Avoid returning a plain dict; keep Map for consistency with State.

         return self.__class__(
             identifier=self.identifier,
-            arguments={**self.arguments, **arguments},
+            arguments=Map({**self.arguments, **arguments}),
             meta=self.meta,
         )

As per coding guidelines.


30-50: Add NumPy‑style docstring to Template (public API).

Document purpose and attributes per guidelines. Example:

 @final
 class Template(State):
+    """
+    Immutable reference to a named template with bound arguments.
+
+    Attributes
+    ----------
+    identifier : str
+        Template identifier to resolve from a repository.
+    arguments : Mapping[str, Multimodal]
+        Default arguments bound to this template.
+    meta : Meta
+        Metadata used when resolving/loading.
+    """
src/draive/multimodal/templates/repository.py (3)

458-458: Use ctx.log_warn instead of ctx.log_warning.

The haiway logging API provides ctx.log_debug, ctx.log_info, ctx.log_warn, and ctx.log_error. The method ctx.log_warning does not exist and will raise AttributeError at runtime.

Apply this diff:

-                                ctx.log_warning(
+                                ctx.log_warn(
                                     "Invalid templates file storage element, skipping..."
                                 )

487-498: Write templates in a stable order for reproducible files.

Dict insertion order can vary, causing non-deterministic file output. Sort declarations by identifier before serializing.

Apply this diff:

+        ordered = sorted(self._declarations.values(), key=lambda d: d.identifier)
         file_contents: bytes = json.dumps(
             [
                 {
-                    "identifier": declaration.identifier,
-                    "description": declaration.description,
-                    "variables": declaration.variables,
-                    "content": self._contents[declaration.identifier],
-                    "meta": declaration.meta.to_mapping(),
+                    "identifier": d.identifier,
+                    "description": d.description,
+                    "variables": d.variables,
+                    "content": self._contents[d.identifier],
+                    "meta": d.meta.to_mapping(),
                 }
-                for declaration in self._declarations.values()
+                for d in ordered
             ]
         ).encode()

313-317: Return templates in a stable order.

Dict ordering can cause non-deterministic behavior in tests and client code. Sort by identifier for reproducibility.

Apply this diff:

-        return tuple(self._declarations.values())
+        return tuple(sorted(self._declarations.values(), key=lambda d: d.identifier))
docs/guides/Postgres.md (1)

84-91: API naming: use resolve_str, not resolve_as_text.

Replace resolve_as_text with resolve_str for consistency with the public API.

-– `resolve(template)` and `resolve_as_text(template)` reuse a cached loader keyed by identifier to
-  pull the latest template body before rendering arguments.
+– `resolve(template)` and `resolve_str(template)` reuse a cached loader keyed by identifier to pull
+  the latest template body before rendering arguments.
src/draive/helpers/instruction_preparation.py (1)

37-41: Use State constructor helper TemplateDeclaration.of for consistency.

Prefer the .of(...) constructor over direct dataclass instantiation.

-                instruction_declaration = TemplateDeclaration(
-                    identifier="instruction",
-                    description=description,
-                    variables={},
-                )
+                instruction_declaration = TemplateDeclaration.of(
+                    identifier="instruction",
+                    description=description,
+                    variables={},
+                )
src/draive/generation/model/default.py (1)

36-62: Pass **extra to TemplatesRepository.resolve_str and unify resolution path.

The past review comment (marked as addressed in commit 1719705) requested:

  1. Passing **extra to TemplatesRepository.resolve_str so backends can access additional context.
  2. Unifying the resolution path so string and Template instructions use consistent placeholder semantics.

However, the current code still:

  • Does not pass **extra to any of the three TemplatesRepository.resolve_str calls (lines 42-46, 55-59, 62).
  • Uses .format() for string instructions (lines 37-39, 50-52) but TemplatesRepository.resolve_str for Templates, creating inconsistent placeholder handling.

Apply the fix from the previous review:

             case "full":
-                if isinstance(instructions, str):
-                    resolved_instructions = instructions.format(
-                        model_schema=generated.json_schema(indent=2),
-                    )
-
-                else:
-                    resolved_instructions = await TemplatesRepository.resolve_str(
-                        instructions.with_arguments(
-                            model_schema=generated.json_schema(indent=2),
-                        )
-                    )
+                schema = generated.json_schema(indent=2)
+                if isinstance(instructions, str):
+                    resolved_instructions = await TemplatesRepository.resolve_str(
+                        Template.of("__inline__", arguments={"model_schema": schema}),
+                        default=instructions,
+                        **extra,
+                    )
+                else:
+                    resolved_instructions = await TemplatesRepository.resolve_str(
+                        instructions.with_arguments(model_schema=schema),
+                        **extra,
+                    )

             case "simplified":
-                if isinstance(instructions, str):
-                    resolved_instructions = instructions.format(
-                        model_schema=generated.simplified_schema(indent=2),
-                    )
-
-                else:
-                    resolved_instructions = await TemplatesRepository.resolve_str(
-                        instructions.with_arguments(
-                            model_schema=generated.simplified_schema(indent=2),
-                        )
-                    )
+                schema = generated.simplified_schema(indent=2)
+                if isinstance(instructions, str):
+                    resolved_instructions = await TemplatesRepository.resolve_str(
+                        Template.of("__inline__", arguments={"model_schema": schema}),
+                        default=instructions,
+                        **extra,
+                    )
+                else:
+                    resolved_instructions = await TemplatesRepository.resolve_str(
+                        instructions.with_arguments(model_schema=schema),
+                        **extra,
+                    )

             case "skip":  # instruction is not modified
-                resolved_instructions = await TemplatesRepository.resolve_str(instructions)
+                resolved_instructions = await TemplatesRepository.resolve_str(
+                    instructions,
+                    **extra,
+                )
src/draive/generation/audio/state.py (1)

20-41: Type-default mismatch: add | str to the union.

The instructions parameter has a default value of "" but the type annotation Template | ModelInstructions does not include str. This was flagged in a previous review as a type-default mismatch.

Add | str to all three signatures.

Apply this diff:

-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",
@@
-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",
@@
-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",
src/draive/conversation/completion/default.py (3)

18-19: Import core types from the public draive.models API.

Imports should use the centralized public exports rather than internal module paths.

Apply this diff:

-from draive.models.types import ModelInstructions, ModelToolRequest
+from draive.models import ModelInstructions, ModelToolRequest

Based on coding guidelines.


96-104: Normalize template variables with to_str() when available.

Avoid str(value) on multimodal values; prefer value.to_str() if present. Building the mapping once improves consistency with the streaming path.

Apply this diff:

+        args = (
+            {
+                key: (
+                    value
+                    if isinstance(value, str)
+                    else (value.to_str() if hasattr(value, "to_str") else str(value))
+                )
+                for key, value in memory_recall.variables.items()
+            }
+            if memory_recall.variables
+            else None
+        )
+
         resolved_instructions: str = await TemplatesRepository.resolve_str(
             instructions,
-            arguments={
-                key: value if isinstance(value, str) else str(value)
-                for key, value in memory_recall.variables.items()
-            }
-            if memory_recall.variables
-            else None,
+            arguments=args,
         )

149-157: Normalize template variables with to_str() when available.

Same concern as in the non-streaming path: prefer value.to_str() over str(value) for multimodal values, and build the mapping once for reuse.

Apply this diff:

+        args = (
+            {
+                key: (
+                    value
+                    if isinstance(value, str)
+                    else (value.to_str() if hasattr(value, "to_str") else str(value))
+                )
+                for key, value in memory_recall.variables.items()
+            }
+            if memory_recall.variables
+            else None
+        )
+
         async for chunk in await GenerativeModel.loop(
-            instructions=await TemplatesRepository.resolve_str(
+            instructions=await TemplatesRepository.resolve_str(
                 instructions,
-                arguments={
-                    key: value if isinstance(value, str) else str(value)
-                    for key, value in memory_recall.variables.items()
-                }
-                if memory_recall.variables
-                else None,
+                arguments=args,
             ),
src/draive/__init__.py (1)

290-291: Remove duplicate ModelInstructions entry.

ModelInstructions appears twice in __all__, which is redundant and may confuse tooling.

Apply this diff:

     "ModelInstructions",
-    "ModelInstructions",
     "ModelMemory",

Based on learnings.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1719705 and e771562.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (44)
  • Makefile (1 hunks)
  • docs/guides/ComprehensiveEvaluation.md (3 hunks)
  • docs/guides/Postgres.md (3 hunks)
  • docs/guides/Templates.md (1 hunks)
  • mkdocs.yml (1 hunks)
  • pyproject.toml (1 hunks)
  • src/draive/__init__.py (3 hunks)
  • src/draive/conversation/completion/default.py (7 hunks)
  • src/draive/conversation/completion/state.py (5 hunks)
  • src/draive/conversation/completion/types.py (4 hunks)
  • src/draive/conversation/realtime/default.py (3 hunks)
  • src/draive/conversation/realtime/state.py (3 hunks)
  • src/draive/conversation/realtime/types.py (2 hunks)
  • src/draive/generation/audio/default.py (1 hunks)
  • src/draive/generation/audio/state.py (4 hunks)
  • src/draive/generation/audio/types.py (2 hunks)
  • src/draive/generation/image/default.py (1 hunks)
  • src/draive/generation/image/state.py (4 hunks)
  • src/draive/generation/image/types.py (2 hunks)
  • src/draive/generation/model/default.py (3 hunks)
  • src/draive/generation/model/state.py (4 hunks)
  • src/draive/generation/model/types.py (2 hunks)
  • src/draive/generation/text/default.py (1 hunks)
  • src/draive/generation/text/state.py (4 hunks)
  • src/draive/generation/text/types.py (2 hunks)
  • src/draive/helpers/instruction_preparation.py (3 hunks)
  • src/draive/helpers/instruction_refinement.py (5 hunks)
  • src/draive/models/__init__.py (0 hunks)
  • src/draive/models/generative.py (5 hunks)
  • src/draive/models/instructions/__init__.py (0 hunks)
  • src/draive/models/instructions/repository.py (0 hunks)
  • src/draive/models/instructions/template.py (0 hunks)
  • src/draive/models/instructions/types.py (0 hunks)
  • src/draive/multimodal/__init__.py (2 hunks)
  • src/draive/multimodal/templates/__init__.py (1 hunks)
  • src/draive/multimodal/templates/repository.py (1 hunks)
  • src/draive/multimodal/templates/types.py (1 hunks)
  • src/draive/multimodal/templates/variables.py (1 hunks)
  • src/draive/openai/images.py (3 hunks)
  • src/draive/postgres/__init__.py (2 hunks)
  • src/draive/postgres/instructions.py (0 hunks)
  • src/draive/postgres/templates.py (1 hunks)
  • src/draive/stages/stage.py (15 hunks)
  • tests/test_multimodal_template_variables.py (1 hunks)
💤 Files with no reviewable changes (6)
  • src/draive/postgres/instructions.py
  • src/draive/models/instructions/init.py
  • src/draive/models/instructions/types.py
  • src/draive/models/instructions/template.py
  • src/draive/models/init.py
  • src/draive/models/instructions/repository.py
🧰 Additional context used
📓 Path-based instructions (13)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use Python 3.12+ features and syntax across the codebase
Format code exclusively with Ruff (make format); do not use other formatters
Skip module-level docstrings

Files:

  • src/draive/stages/stage.py
  • src/draive/helpers/instruction_refinement.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/multimodal/__init__.py
  • src/draive/conversation/realtime/types.py
  • src/draive/helpers/instruction_preparation.py
  • src/draive/multimodal/templates/variables.py
  • src/draive/generation/audio/state.py
  • src/draive/generation/text/types.py
  • src/draive/postgres/templates.py
  • src/draive/openai/images.py
  • src/draive/conversation/realtime/default.py
  • src/draive/multimodal/templates/repository.py
  • src/draive/generation/model/state.py
  • src/draive/conversation/completion/types.py
  • src/draive/generation/text/state.py
  • src/draive/generation/audio/default.py
  • src/draive/conversation/completion/state.py
  • src/draive/models/generative.py
  • src/draive/generation/model/default.py
  • src/draive/generation/model/types.py
  • src/draive/generation/audio/types.py
  • src/draive/conversation/completion/default.py
  • src/draive/generation/image/default.py
  • src/draive/__init__.py
  • tests/test_multimodal_template_variables.py
  • src/draive/conversation/realtime/state.py
  • src/draive/generation/image/types.py
  • src/draive/generation/text/default.py
  • src/draive/postgres/__init__.py
  • src/draive/generation/image/state.py
  • src/draive/multimodal/templates/types.py
src/draive/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/**/*.py: Import Haiway symbols directly (from haiway import State, ctx)
Use ctx.scope(...) to bind scoped Disposables and active State; avoid global state
Route all logs through ctx.log_debug/info/warn/error; do not use print
Use latest, most strict typing syntax (Python 3.12+), with strict typing only for public APIs
Avoid loose Any except at explicit third‑party boundaries
Prefer explicit attribute access with static types; avoid dynamic getattr except at narrow boundaries
Prefer Mapping/Sequence/Iterable in public types over dict/list/set
Use final where applicable; avoid inheritance and prefer composition
Use precise unions (|) and narrow with match/isinstance; avoid cast unless provably safe and localized
Model immutable data/config and facades with haiway.State; provide ergonomic classmethods like .of(...)
Avoid in-place mutation; use State.updated(...) or functional builders to produce new instances
Access active state via haiway.ctx inside async scopes (ctx.scope(...))
Use @statemethod for public state methods that dispatch on the active instance
Log around generation calls, tool dispatch, and provider requests/responses without leaking secrets; prefer structured/concise messages
Add metrics via ctx.record where applicable
All I/O is async; keep boundaries async and use ctx.spawn for detached tasks
Use structured concurrency and valid coroutine usage; rely on haiway/asyncio; avoid custom threading
Construct multimodal content with MultimodalContent.of(...) and compose blocks explicitly
Use ResourceContent/ResourceReference for media/data blobs
Wrap custom types/data within ArtifactContent; use hidden when needed
Add NumPy-style docstrings for public symbols with Parameters/Returns/Raises and rationale when non-obvious
Avoid docstrings on internal helpers; keep names self-explanatory
Keep docstrings high-quality; mkdocstrings pulls them into API reference
Never log secrets or full request bodies containing keys/tokens

Files:

  • src/draive/stages/stage.py
  • src/draive/helpers/instruction_refinement.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/multimodal/__init__.py
  • src/draive/conversation/realtime/types.py
  • src/draive/helpers/instruction_preparation.py
  • src/draive/multimodal/templates/variables.py
  • src/draive/generation/audio/state.py
  • src/draive/generation/text/types.py
  • src/draive/postgres/templates.py
  • src/draive/openai/images.py
  • src/draive/conversation/realtime/default.py
  • src/draive/multimodal/templates/repository.py
  • src/draive/generation/model/state.py
  • src/draive/conversation/completion/types.py
  • src/draive/generation/text/state.py
  • src/draive/generation/audio/default.py
  • src/draive/conversation/completion/state.py
  • src/draive/models/generative.py
  • src/draive/generation/model/default.py
  • src/draive/generation/model/types.py
  • src/draive/generation/audio/types.py
  • src/draive/conversation/completion/default.py
  • src/draive/generation/image/default.py
  • src/draive/__init__.py
  • src/draive/conversation/realtime/state.py
  • src/draive/generation/image/types.py
  • src/draive/generation/text/default.py
  • src/draive/postgres/__init__.py
  • src/draive/generation/image/state.py
  • src/draive/multimodal/templates/types.py
src/draive/stages/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement pipeline stage abstractions and helpers under draive/stages/

Files:

  • src/draive/stages/stage.py
src/draive/multimodal/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement content types and helpers (MultimodalContent, TextContent, ArtifactContent) under draive/multimodal/

Files:

  • src/draive/multimodal/templates/__init__.py
  • src/draive/multimodal/__init__.py
  • src/draive/multimodal/templates/variables.py
  • src/draive/multimodal/templates/repository.py
  • src/draive/multimodal/templates/types.py
src/draive/conversation/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement higher-level chat/realtime conversations under draive/conversation/

Files:

  • src/draive/conversation/realtime/types.py
  • src/draive/conversation/realtime/default.py
  • src/draive/conversation/completion/types.py
  • src/draive/conversation/completion/state.py
  • src/draive/conversation/completion/default.py
  • src/draive/conversation/realtime/state.py
mkdocs.yml

📄 CodeRabbit inference engine (AGENTS.md)

Register documentation navigation updates in mkdocs.yml (nav section) when adding/adjusting pages

Files:

  • mkdocs.yml
docs/**/*

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*: When behavior/API changes, update relevant docs under docs/ and examples as applicable
When adding public APIs, update examples/guides and ensure cross-links render

Files:

  • docs/guides/Postgres.md
  • docs/guides/Templates.md
  • docs/guides/ComprehensiveEvaluation.md
src/draive/{httpx,mcp,postgres,opentelemetry}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Place integrations under draive/httpx, draive/mcp, draive/postgres, draive/opentelemetry

Files:

  • src/draive/postgres/templates.py
  • src/draive/postgres/__init__.py
src/draive/{openai,anthropic,mistral,gemini,vllm,ollama,bedrock,cohere}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/{openai,anthropic,mistral,gemini,vllm,ollama,bedrock,cohere}/**/*.py: Provider-specific feature modules live under their respective provider directories
Translate provider/SDK errors into typed exceptions; do not raise bare Exception and preserve context
Use environment variables for credentials and resolve via helper functions like getenv_str

Files:

  • src/draive/openai/images.py
src/draive/models/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Keep core abstractions (GenerativeModel, tools, instructions) under draive/models/

Files:

  • src/draive/models/generative.py
{pyproject.toml,pyrightconfig.json}

📄 CodeRabbit inference engine (AGENTS.md)

Use Ruff, Bandit, and Pyright (strict) via make lint

Files:

  • pyproject.toml
src/draive/__init__.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/__init__.py: Centralize public exports in src/draive/init.py
Update src/draive/init.py exports when API surface changes

Files:

  • src/draive/__init__.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.py: Do not perform real network I/O in unit tests; mock providers/HTTP
Keep tests fast and focused on changed code; start with unit tests around new types/functions/adapters
Use fixtures from tests/ or add focused ones; avoid heavy integration scaffolding
Use pytest-asyncio for coroutine tests (@pytest.mark.asyncio)
Prefer scoping with ctx.scope(...) in async tests and bind required State instances explicitly
Avoid real I/O and network in async tests; stub provider calls and HTTP

Files:

  • tests/test_multimodal_template_variables.py
🧠 Learnings (6)
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/models/**/*.py : Keep core abstractions (GenerativeModel, tools, instructions) under draive/models/

Applied to files:

  • src/draive/stages/stage.py
  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/__init__.py : Centralize public exports in src/draive/__init__.py

Applied to files:

  • src/draive/multimodal/templates/__init__.py
  • src/draive/multimodal/__init__.py
  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/multimodal/**/*.py : Implement content types and helpers (MultimodalContent, TextContent, ArtifactContent) under draive/multimodal/

Applied to files:

  • src/draive/multimodal/__init__.py
  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/**/*.py : Import Haiway symbols directly (from haiway import State, ctx)

Applied to files:

  • src/draive/helpers/instruction_preparation.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/**/*.py : Add NumPy-style docstrings for public symbols with Parameters/Returns/Raises and rationale when non-obvious

Applied to files:

  • src/draive/multimodal/templates/repository.py
  • src/draive/multimodal/templates/types.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/__init__.py : Update src/draive/__init__.py exports when API surface changes

Applied to files:

  • src/draive/__init__.py
🧬 Code graph analysis (31)
src/draive/stages/stage.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-306)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/helpers/instruction_refinement.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-306)
  • loading (319-325)
  • loading (394-404)
  • load (247-262)
src/draive/multimodal/templates/__init__.py (2)
src/draive/multimodal/templates/repository.py (4)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
  • TemplatesRepository (61-306)
src/draive/multimodal/templates/types.py (6)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateDefining (123-132)
  • TemplateListing (105-109)
  • TemplateLoading (113-119)
  • TemplateMissing (18-27)
src/draive/multimodal/__init__.py (2)
src/draive/multimodal/templates/repository.py (4)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
  • TemplatesRepository (61-306)
src/draive/multimodal/templates/types.py (6)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateDefining (123-132)
  • TemplateListing (105-109)
  • TemplateLoading (113-119)
  • TemplateMissing (18-27)
src/draive/conversation/realtime/types.py (2)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/helpers/instruction_preparation.py (1)
src/draive/multimodal/templates/types.py (1)
  • TemplateDeclaration (80-101)
src/draive/multimodal/templates/variables.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (2)
  • of (33-45)
  • of (82-96)
src/draive/generation/audio/state.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/text/types.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/postgres/templates.py (3)
src/draive/multimodal/templates/repository.py (11)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
  • TemplatesRepository (61-306)
  • listing (313-317)
  • listing (384-392)
  • load (247-262)
  • loading (319-325)
  • loading (394-404)
  • defining (327-342)
  • defining (406-425)
src/draive/multimodal/templates/types.py (1)
  • TemplateDeclaration (80-101)
src/draive/parameters/model.py (2)
  • from_json (454-469)
  • to_json (501-517)
src/draive/openai/images.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-306)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/conversation/realtime/default.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/multimodal/templates/repository.py (3)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (7)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateDefining (123-132)
  • TemplateLoading (113-119)
  • TemplateMissing (18-27)
  • of (33-45)
  • of (82-96)
src/draive/multimodal/templates/variables.py (3)
  • parse_template_variables (14-21)
  • resolve_multimodal_template (48-67)
  • resolve_text_template (24-45)
src/draive/generation/model/state.py (4)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/conversation/completion/types.py (2)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/text/state.py (3)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/audio/default.py (3)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/models/generative.py (5)
  • completion (62-71)
  • completion (74-83)
  • completion (87-96)
  • completion (99-108)
  • completion (111-181)
src/draive/conversation/completion/state.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/model/default.py (5)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (2)
  • Template (31-76)
  • with_arguments (51-62)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-306)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/parameters/model.py (2)
  • json_schema (443-451)
  • simplified_schema (433-440)
src/draive/parameters/schema.py (1)
  • simplified_schema (9-26)
src/draive/generation/model/types.py (3)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/audio/types.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/conversation/completion/default.py (3)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-306)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/models/generative.py (6)
  • GenerativeModel (45-515)
  • loop (185-194)
  • loop (197-206)
  • loop (210-219)
  • loop (222-231)
  • loop (234-306)
src/draive/generation/image/default.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/__init__.py (2)
src/draive/multimodal/templates/types.py (3)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateMissing (18-27)
src/draive/multimodal/templates/repository.py (1)
  • TemplatesRepository (61-306)
tests/test_multimodal_template_variables.py (3)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/variables.py (3)
  • parse_template_variables (14-21)
  • resolve_multimodal_template (48-67)
  • resolve_text_template (24-45)
src/draive/multimodal/text.py (1)
  • TextContent (11-82)
src/draive/conversation/realtime/state.py (3)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/image/types.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/text/default.py (3)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/models/generative.py (5)
  • loop (185-194)
  • loop (197-206)
  • loop (210-219)
  • loop (222-231)
  • loop (234-306)
src/draive/postgres/__init__.py (1)
src/draive/postgres/templates.py (1)
  • PostgresTemplatesRepository (28-173)
src/draive/generation/image/state.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/types.py (1)
src/draive/stages/stage.py (1)
  • with_meta (1390-1435)
🪛 LanguageTool
docs/guides/Templates.md

[style] ~104-~104: It’s more common nowadays to write this noun as one word.
Context: ...fining templates programmatically, pass variables={"user": "User name"} to document expected arguments. This...

(RECOMMENDED_COMPOUNDS)

🔇 Additional comments (38)
pyproject.toml (1)

8-8: Version bump is appropriate for a minor feature release.

The version increment from 0.89.5 to 0.90.0 correctly reflects a minor version bump for the Templates subsystem feature addition, following semantic versioning conventions. No breaking changes to external dependencies or tool configurations are introduced.

src/draive/stages/stage.py (2)

49-50: Import update looks good.

Switch to Template/TemplatesRepository aligns with the new Templates subsystem.


293-299: Review comment is incorrect; no type mismatch exists.

Since ModelInstructions is a type alias for str, passing Template | ModelInstructions to a function expecting Template | str is type-safe. Pyright treats type aliases as their underlying types, so Template | ModelInstructions is compatible with Template | str without widening TemplatesRepository.resolve_str or adding casts.

No changes needed.

Likely an incorrect or invalid review comment.

docs/guides/ComprehensiveEvaluation.md (1)

3-5: LGTM — formatting only.

Readability improvements; no semantic changes.

Also applies to: 9-14, 66-68, 282-283

src/draive/helpers/instruction_refinement.py (3)

13-41: LGTM!

The migration from Instructions to Templates is correctly applied. The instructions parameter now accepts Template while instructions_content holds the actual string content.


103-158: LGTM!

The patched_instructions_repository correctly overrides template loading for the refined instructions while preserving access to other templates. The pattern properly isolates refined content during evaluation.


223-280: LGTM!

The template loading correctly uses TemplatesRepository.load as a classmethod (enabled by @statemethod). The initialization properly handles both explicit instructions_content and on-demand template loading.

mkdocs.yml (1)

101-101: LGTM!

The Templates guide is correctly added to the navigation structure under the Guides section.

src/draive/generation/image/types.py (1)

10-18: LGTM!

The parameter type correctly broadens to Template | ModelInstructions, aligning with the Templates migration across the generation subsystem.

src/draive/multimodal/__init__.py (1)

8-33: LGTM!

The Template-related symbols are correctly imported and exported, expanding the public API surface. This aligns with the coding guideline to centralize public exports.

As per coding guidelines.

src/draive/generation/audio/default.py (1)

17-29: LGTM!

The function correctly migrates to accept Template | ModelInstructions and resolves instructions via TemplatesRepository.resolve_str. This aligns with the Templates subsystem migration.

src/draive/generation/model/types.py (1)

23-36: LGTM!

The ModelGenerating protocol correctly updates the instructions parameter to Template | ModelInstructions, maintaining consistency with the broader Templates migration.

src/draive/conversation/realtime/types.py (1)

123-134: LGTM!

The RealtimeConversationPreparing protocol correctly updates the instructions parameter to Template | ModelInstructions, aligning with the Templates subsystem migration across conversation components.

src/draive/multimodal/templates/__init__.py (1)

1-19: LGTM! Clean module exports.

The template subsystem exports are well-organized and complete. All symbols are properly imported and exposed via __all__.

Based on learnings: Consider also centralizing these exports in src/draive/__init__.py for top-level access, as noted in the learning that public exports should be centralized at the top level.

src/draive/generation/audio/types.py (2)

3-5: LGTM! Import changes align with template migration.

The replacement of ResolveableInstructions with ModelInstructions and addition of Template correctly supports the new template subsystem.


15-15: LGTM! Type signature correctly updated.

The parameter type Template | ModelInstructions properly reflects the new instruction format, allowing callers to pass either a template reference or direct model instructions.

src/draive/postgres/__init__.py (2)

12-12: LGTM! Import correctly updated for template migration.

The import of PostgresTemplatesRepository from the new templates module aligns with the subsystem refactoring.


23-23: LGTM! Export correctly reflects template subsystem.

The replacement of PostgresInstructionsRepository with PostgresTemplatesRepository in the public API surface is consistent with the migration to the templates-based workflow.

tests/test_multimodal_template_variables.py (3)

1-86: LGTM! Comprehensive parsing tests.

The parse_template_variables test suite thoroughly covers:

  • Unique name extraction with repeated placeholders
  • Adjacent placeholders without separators
  • Symbolic names (hyphens, numbers)
  • Edge cases: empty templates, malformed syntax, whitespace handling, literal braces

88-127: LGTM! Text resolution tests are thorough.

The resolve_text_template tests correctly validate:

  • Placeholder substitution with string arguments
  • Repeated placeholder expansion
  • Missing argument detection (KeyError)
  • Unused arguments (correctly ignored per implementation)
  • Edge positions and empty templates

All calls correctly use arguments=... keyword syntax.


129-207: LGTM! Multimodal resolution tests are excellent.

The resolve_multimodal_template tests comprehensively verify:

  • String and TextContent concatenation into MultimodalContent
  • Nested content flattening
  • Metadata boundary preservation across text parts
  • Missing/unused argument handling
  • Empty template edge case

The metadata preservation test (lines 180-202) is particularly valuable for validating multimodal correctness.

src/draive/generation/text/default.py (3)

9-13: LGTM! Imports correctly updated for template subsystem.

The removal of InstructionsRepository and addition of Template and TemplatesRepository align with the migration to the templates-based instruction resolution.


20-20: LGTM! Parameter type correctly broadened.

The Template | ModelInstructions union allows callers to pass either template references or direct model instructions, providing flexibility while maintaining type safety.


28-28: LGTM! Resolution correctly delegates to TemplatesRepository.

The switch from InstructionsRepository.resolve to TemplatesRepository.resolve_str properly integrates with the new template resolution pipeline while flattening to the string format required by the model.

docs/guides/Templates.md (1)

1-122: LGTM! Comprehensive and well-structured documentation.

This guide effectively documents the Templates subsystem:

  • Clear explanation of template basics and immutable API design
  • Coverage of all storage backends (volatile, file, Postgres, custom)
  • Practical examples for resolution and runtime usage
  • Migration guidance from InstructionsRepository with placeholder syntax changes

The migration section (lines 107-120) is particularly valuable for developers updating existing code.

src/draive/generation/image/default.py (3)

8-11: LGTM! Imports correctly updated.

The import changes align with the template migration pattern used consistently across other generation modules.


19-19: LGTM! Parameter type correctly updated.

The Template | ModelInstructions type properly reflects the new instruction format for image generation.


25-25: LGTM! Resolution correctly integrated.

The use of TemplatesRepository.resolve_str properly resolves templates while maintaining the text-based instruction format required by the image generation flow.

src/draive/conversation/realtime/state.py (3)

14-22: LGTM! Imports correctly updated.

The addition of ModelInstructions and Template imports aligns with the type signature update for the prepare method.


40-40: LGTM! Parameter type correctly broadened.

The Template | ModelInstructions union provides flexibility for callers while maintaining type safety in realtime conversation preparation.


50-50: LGTM! Docstring correctly reflects parameter type.

The parameter documentation accurately describes the updated instructions type.

src/draive/openai/images.py (1)

24-24: Code is correct; no runtime type incompatibility exists.

ModelInstructions is a direct type alias to str (line 76, src/draive/models/types.py), making Template | ModelInstructions equivalent to Template | str at runtime. This matches the parameter type expected by resolve_str, which accepts Template | str. No type coercion or verification is needed.

src/draive/conversation/completion/types.py (1)

26-55: LGTM!

The Protocol definition correctly updates the type signatures to accept Template | ModelInstructions. Since this is a protocol with no default values, there are no type-default mismatches.

src/draive/conversation/completion/default.py (3)

27-27: LGTM!

The function signature correctly accepts Template | ModelInstructions, aligning with the new Templates subsystem.

Also applies to: 39-39, 50-50


82-82: LGTM!

Internal function signatures correctly updated to match the public API.

Also applies to: 135-135


106-111: LGTM!

The resolved instruction string is correctly passed to GenerativeModel.loop.

src/draive/__init__.py (2)

178-181: LGTM!

The Template family is correctly imported from draive.multimodal for public re-export.


351-354: LGTM!

The Template family is correctly exposed in __all__, properly extending the public API surface.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/draive/helpers/instruction_preparation.py (1)

46-46: Validate description before assert to prevent runtime errors.

The function accepts TemplateDeclaration which allows description=None, but line 46 asserts it must not be None. When a caller passes a TemplateDeclaration with description=None, this assert will fail without a clear error message. Validate upfront and raise a descriptive ValueError instead.

Apply this diff to add proper validation:

         case declaration:
             instruction_declaration = declaration
 
+    if instruction_declaration.description is None:
+        raise ValueError("TemplateDeclaration must have a non-None description")
+
-    assert instruction_declaration.description is not None  # nosec: B101
     result: MultimodalContent = await Stage.completion(
src/draive/models/generative.py (1)

620-642: Update docstring to reflect actual parameter type and behavior: instructions accepts only ModelInstructions, with no in-method resolution.

The docstring at line 656 incorrectly claims the parameter accepts Template | ModelInstructions and that the method resolves instructions via InstructionsRepository. However:

  • All three signatures (lines 620, 631, 642) declare instructions: ModelInstructions = ""
  • The implementation passes instructions directly to session_preparing (line 694) without any resolution
  • Call sites (e.g., src/draive/conversation/realtime/default.py:52) resolve templates upstream via TemplatesRepository.resolve_str(...) before calling .session()

Update the docstring parameter description to: instructions : ModelInstructions, optional and remove the false claim about in-method resolution.

♻️ Duplicate comments (18)
src/draive/helpers/instruction_preparation.py (2)

25-30: Add a NumPy-style docstring to this public function.

This public helper requires documentation of parameters, return value, and raises behavior per coding guidelines.

As per coding guidelines.


37-41: Use TemplateDeclaration.of() for consistency.

Direct dataclass construction should be replaced with the State constructor helper.

As per coding guidelines.

src/draive/multimodal/templates/types.py (5)

18-27: Add NumPy-style docstring for public exception class.

TemplateMissing is a public exception but lacks a docstring explaining its purpose and the identifier parameter.

As per coding guidelines.


30-76: Add NumPy-style docstrings for Template class and its methods.

The Template class and its public methods (of, with_arguments, with_meta) lack docstrings. Per coding guidelines, public API symbols require NumPy-style docstrings with Parameters/Returns sections.

As per coding guidelines.


79-101: Add NumPy-style docstrings and prefer Map() for variables.

TemplateDeclaration lacks docstrings and initializes variables with {} instead of Map(). For consistency with the immutability pattern used elsewhere in the codebase, consider using Map().

As per coding guidelines.


104-132: Add NumPy-style docstrings for runtime-checkable protocols.

The three protocols (TemplateListing, TemplateLoading, TemplateDefining) lack docstrings explaining their purpose, parameters, and return values.

As per coding guidelines.


51-62: Use Map() for consistency with State immutability semantics.

Line 60 constructs a plain dict, but the of method at line 43 passes the raw dict/mapping directly. For consistency with haiway's State immutability guarantees, consider whether arguments should be wrapped in Map() at construction time.

docs/guides/Postgres.md (1)

88-89: Use correct API method name: resolve_str, not resolve_as_text.

The documentation references resolve_as_text, but the actual TemplatesRepository API method is resolve_str.

Apply this diff:

-- `resolve(template)` and `resolve_as_text(template)` reuse a cached loader keyed by identifier to
-  pull the latest template body before rendering arguments.
+- `resolve(template)` and `resolve_str(template)` reuse a cached loader keyed by identifier to
+  pull the latest template body before rendering arguments.
src/draive/postgres/templates.py (1)

74-82: Prefer TemplateDeclaration.of() for consistency.

Direct construction of TemplateDeclaration bypasses the conventional factory pattern used throughout the codebase. Using TemplateDeclaration.of() ensures consistent state initialization and future-proofing.

Apply this diff:

         return tuple(
-            TemplateDeclaration(
-                identifier=cast(str, result["identifier"]),
+            TemplateDeclaration.of(
+                cast(str, result["identifier"]),
                 description=cast(str | None, result["description"]),
                 variables=json.loads(cast(str, result["variables"] or "{}")),
                 meta=Meta.from_json(cast(str, result["meta"] or "{}")),
             )
             for result in results
         )
src/draive/generation/text/types.py (1)

11-20: Consider adding a docstring to the protocol.

For API reference completeness, add a brief docstring documenting the protocol's parameters and return type.

As per coding guidelines

@runtime_checkable
class TextGenerating(Protocol):
    """Generate text given instructions, input, toolbox, and examples.
    
    Parameters
    ----------
    instructions : Template | ModelInstructions
        Prompt template or direct instructions for generation.
    input : MultimodalContent
        Input content to process.
    toolbox : Toolbox
        Available tools for model invocation.
    examples : Iterable[tuple[MultimodalContent, str]]
        Example input-output pairs for few-shot learning.
    **extra : Any
        Additional provider-specific parameters.
        
    Returns
    -------
    str
        Generated text output.
    """
    async def __call__(
        self,
        *,
        instructions: Template | ModelInstructions,
        input: MultimodalContent,  # noqa: A002
        toolbox: Toolbox,
        examples: Iterable[tuple[MultimodalContent, str]],
        **extra: Any,
    ) -> str: ...
src/draive/generation/audio/state.py (1)

20-20: Type-default mismatch: include str in the union.

All three signatures provide a default of "" (str), but the type annotation Template | ModelInstructions does not include str. This causes a type inconsistency.

Apply this fix to all three signatures:

-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",

Also applies to: 29-29, 38-38

src/draive/conversation/realtime/default.py (1)

52-56: Pass **extra into resolve_str to enable backend-aware resolution.

Propagate the received context to the repository. Previously noted.

Apply:

-        instructions=await TemplatesRepository.resolve_str(instructions),
+        instructions=await TemplatesRepository.resolve_str(instructions, **extra),
src/draive/generation/model/default.py (1)

34-63: Unify resolution via TemplatesRepository.resolve_str and pass **extra.

Avoid mixing .format with template resolution and ensure backends receive context. This was flagged earlier.

Apply:

@@
         match schema_injection:
             case "full":
-                if isinstance(instructions, str):
-                    resolved_instructions = instructions.format(
-                        model_schema=generated.json_schema(indent=2),
-                    )
-                else:
-                    resolved_instructions = await TemplatesRepository.resolve_str(
-                        instructions.with_arguments(
-                            model_schema=generated.json_schema(indent=2),
-                        )
-                    )
+                schema = generated.json_schema(indent=2)
+                if isinstance(instructions, str):
+                    resolved_instructions = await TemplatesRepository.resolve_str(
+                        Template.of("__inline__", arguments={"model_schema": schema}),
+                        default=instructions,
+                        **extra,
+                    )
+                else:
+                    resolved_instructions = await TemplatesRepository.resolve_str(
+                        instructions.with_arguments(model_schema=schema),
+                        **extra,
+                    )
 
             case "simplified":
-                if isinstance(instructions, str):
-                    resolved_instructions = instructions.format(
-                        model_schema=generated.simplified_schema(indent=2),
-                    )
-                else:
-                    resolved_instructions = await TemplatesRepository.resolve_str(
-                        instructions.with_arguments(
-                            model_schema=generated.simplified_schema(indent=2),
-                        )
-                    )
+                schema = generated.simplified_schema(indent=2)
+                if isinstance(instructions, str):
+                    resolved_instructions = await TemplatesRepository.resolve_str(
+                        Template.of("__inline__", arguments={"model_schema": schema}),
+                        default=instructions,
+                        **extra,
+                    )
+                else:
+                    resolved_instructions = await TemplatesRepository.resolve_str(
+                        instructions.with_arguments(model_schema=schema),
+                        **extra,
+                    )
 
             case "skip":  # instruction is not modified
-                resolved_instructions = await TemplatesRepository.resolve_str(instructions)
+                resolved_instructions = await TemplatesRepository.resolve_str(
+                    instructions,
+                    **extra,
+                )
#!/bin/bash
# List all resolve_str calls with context to spot missing **extra
rg -nC2 -P 'TemplatesRepository\.resolve_str\('

# Grep for remaining direct string formatting of model_schema
rg -nP -C2 '\.format\s*\(\s*model_schema='
src/draive/conversation/completion/default.py (1)

97-105: Prefer to_str() for multimodal variable conversion.

The template variable mapping uses str(value) for all non-string values. For Multimodal types that implement to_str(), this method should be preferred as it provides proper multimodal-to-text conversion semantics.

Apply this pattern:

         resolved_instructions: str = await TemplatesRepository.resolve_str(
             instructions,
             arguments={
-                key: value if isinstance(value, str) else str(value)
+                key: (
+                    value
+                    if isinstance(value, str)
+                    else (value.to_str() if hasattr(value, "to_str") else str(value))
+                )
                 for key, value in memory_recall.variables.items()
             }
             if memory_recall.variables
             else None,
         )

Also apply the same pattern at lines 150-158 in the streaming path.

Based on coding guidelines.

src/draive/multimodal/templates/repository.py (4)

457-460: Use ctx.log_warn instead of ctx.log_warning (runtime error risk).

The haiway logging API is ctx.log_debug/info/warn/error. ctx.log_warning does not exist and will raise AttributeError at runtime.

Apply this diff:

                             case _:  # skip with warning
-                                ctx.log_warning(
+                                ctx.log_warn(
                                     "Invalid templates file storage element, skipping..."
                                 )

313-318: Return templates in a stable order.

Dict value ordering can cause non-deterministic behavior. Sort by identifier for reproducibility.

Apply this diff:

     async def listing(
         self,
         **extra: Any,
     ) -> Sequence[TemplateDeclaration]:
-        return tuple(self._declarations.values())
+        return tuple(sorted(self._declarations.values(), key=lambda d: d.identifier))

384-392: Return templates in a stable order.

Dict value ordering can cause non-deterministic behavior. Sort by identifier for reproducibility.

Apply this diff:

     async def listing(
         self,
         **extra: Any,
     ) -> Sequence[TemplateDeclaration]:
         if self._declarations is None:
             await self._load_file()
 
         assert self._declarations is not None  # nosec: B101
-        return tuple(self._declarations.values())
+        return tuple(sorted(self._declarations.values(), key=lambda d: d.identifier))

483-501: Write templates in a stable order for reproducible files.

Non-deterministic dict ordering causes diff noise and non-reproducible file output. Sort declarations by identifier and use sort_keys=True in json.dumps.

Apply this diff:

     async def _save_file(self) -> None:
         if self._declarations is None or self._contents is None:
             return  # nothing to save
 
+        ordered = sorted(self._declarations.values(), key=lambda d: d.identifier)
         file_contents: bytes = json.dumps(
             [
                 {
-                    "identifier": declaration.identifier,
-                    "description": declaration.description,
-                    "variables": declaration.variables,
-                    "content": self._contents[declaration.identifier],
-                    "meta": declaration.meta.to_mapping(),
+                    "identifier": d.identifier,
+                    "description": d.description,
+                    "variables": d.variables,
+                    "content": self._contents[d.identifier],
+                    "meta": d.meta.to_mapping(),
                 }
-                for declaration in self._declarations.values()
-            ]
+                for d in ordered
+            ],
+            sort_keys=True,
         ).encode()
 
         async with ctx.disposables(FileAccess.open(self._path, create=True)):
             await File.write(file_contents)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e771562 and 031f8d4.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (48)
  • Makefile (1 hunks)
  • docs/guides/ComprehensiveEvaluation.md (3 hunks)
  • docs/guides/Postgres.md (3 hunks)
  • docs/guides/Templates.md (1 hunks)
  • mkdocs.yml (1 hunks)
  • pyproject.toml (1 hunks)
  • src/draive/__init__.py (2 hunks)
  • src/draive/conversation/completion/default.py (7 hunks)
  • src/draive/conversation/completion/state.py (5 hunks)
  • src/draive/conversation/completion/types.py (4 hunks)
  • src/draive/conversation/realtime/default.py (3 hunks)
  • src/draive/conversation/realtime/state.py (3 hunks)
  • src/draive/conversation/realtime/types.py (2 hunks)
  • src/draive/generation/audio/default.py (1 hunks)
  • src/draive/generation/audio/state.py (4 hunks)
  • src/draive/generation/audio/types.py (2 hunks)
  • src/draive/generation/image/default.py (1 hunks)
  • src/draive/generation/image/state.py (4 hunks)
  • src/draive/generation/image/types.py (2 hunks)
  • src/draive/generation/model/default.py (3 hunks)
  • src/draive/generation/model/state.py (4 hunks)
  • src/draive/generation/model/types.py (2 hunks)
  • src/draive/generation/text/default.py (1 hunks)
  • src/draive/generation/text/state.py (4 hunks)
  • src/draive/generation/text/types.py (2 hunks)
  • src/draive/helpers/instruction_preparation.py (3 hunks)
  • src/draive/helpers/instruction_refinement.py (5 hunks)
  • src/draive/models/__init__.py (0 hunks)
  • src/draive/models/generative.py (5 hunks)
  • src/draive/models/instructions/__init__.py (0 hunks)
  • src/draive/models/instructions/repository.py (0 hunks)
  • src/draive/models/instructions/template.py (0 hunks)
  • src/draive/models/instructions/types.py (0 hunks)
  • src/draive/multimodal/__init__.py (2 hunks)
  • src/draive/multimodal/templates/__init__.py (1 hunks)
  • src/draive/multimodal/templates/repository.py (1 hunks)
  • src/draive/multimodal/templates/types.py (1 hunks)
  • src/draive/multimodal/templates/variables.py (1 hunks)
  • src/draive/openai/images.py (3 hunks)
  • src/draive/openai/responses.py (1 hunks)
  • src/draive/postgres/__init__.py (2 hunks)
  • src/draive/postgres/instructions.py (0 hunks)
  • src/draive/postgres/memory.py (1 hunks)
  • src/draive/postgres/templates.py (1 hunks)
  • src/draive/stages/stage.py (15 hunks)
  • tests/test_generative_merge.py (1 hunks)
  • tests/test_multimodal_template_variables.py (1 hunks)
  • tests/test_toolbox.py (1 hunks)
💤 Files with no reviewable changes (6)
  • src/draive/models/instructions/repository.py
  • src/draive/models/instructions/init.py
  • src/draive/models/init.py
  • src/draive/postgres/instructions.py
  • src/draive/models/instructions/template.py
  • src/draive/models/instructions/types.py
🧰 Additional context used
📓 Path-based instructions (13)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use Python 3.12+ features and syntax across the codebase
Format code exclusively with Ruff (make format); do not use other formatters
Skip module-level docstrings

Files:

  • src/draive/generation/model/types.py
  • tests/test_generative_merge.py
  • src/draive/multimodal/__init__.py
  • src/draive/helpers/instruction_preparation.py
  • src/draive/postgres/templates.py
  • src/draive/generation/audio/state.py
  • tests/test_multimodal_template_variables.py
  • src/draive/conversation/realtime/types.py
  • src/draive/openai/responses.py
  • src/draive/openai/images.py
  • src/draive/postgres/memory.py
  • src/draive/generation/model/state.py
  • src/draive/generation/text/types.py
  • src/draive/generation/text/state.py
  • src/draive/multimodal/templates/types.py
  • src/draive/conversation/realtime/default.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/generation/model/default.py
  • src/draive/generation/image/default.py
  • src/draive/generation/audio/types.py
  • src/draive/generation/text/default.py
  • src/draive/multimodal/templates/variables.py
  • src/draive/conversation/completion/default.py
  • src/draive/conversation/completion/state.py
  • src/draive/conversation/realtime/state.py
  • src/draive/helpers/instruction_refinement.py
  • src/draive/postgres/__init__.py
  • src/draive/conversation/completion/types.py
  • tests/test_toolbox.py
  • src/draive/generation/audio/default.py
  • src/draive/generation/image/state.py
  • src/draive/__init__.py
  • src/draive/models/generative.py
  • src/draive/stages/stage.py
  • src/draive/generation/image/types.py
  • src/draive/multimodal/templates/repository.py
src/draive/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/**/*.py: Import Haiway symbols directly (from haiway import State, ctx)
Use ctx.scope(...) to bind scoped Disposables and active State; avoid global state
Route all logs through ctx.log_debug/info/warn/error; do not use print
Use latest, most strict typing syntax (Python 3.12+), with strict typing only for public APIs
Avoid loose Any except at explicit third‑party boundaries
Prefer explicit attribute access with static types; avoid dynamic getattr except at narrow boundaries
Prefer Mapping/Sequence/Iterable in public types over dict/list/set
Use final where applicable; avoid inheritance and prefer composition
Use precise unions (|) and narrow with match/isinstance; avoid cast unless provably safe and localized
Model immutable data/config and facades with haiway.State; provide ergonomic classmethods like .of(...)
Avoid in-place mutation; use State.updated(...) or functional builders to produce new instances
Access active state via haiway.ctx inside async scopes (ctx.scope(...))
Use @statemethod for public state methods that dispatch on the active instance
Log around generation calls, tool dispatch, and provider requests/responses without leaking secrets; prefer structured/concise messages
Add metrics via ctx.record where applicable
All I/O is async; keep boundaries async and use ctx.spawn for detached tasks
Use structured concurrency and valid coroutine usage; rely on haiway/asyncio; avoid custom threading
Construct multimodal content with MultimodalContent.of(...) and compose blocks explicitly
Use ResourceContent/ResourceReference for media/data blobs
Wrap custom types/data within ArtifactContent; use hidden when needed
Add NumPy-style docstrings for public symbols with Parameters/Returns/Raises and rationale when non-obvious
Avoid docstrings on internal helpers; keep names self-explanatory
Keep docstrings high-quality; mkdocstrings pulls them into API reference
Never log secrets or full request bodies containing keys/tokens

Files:

  • src/draive/generation/model/types.py
  • src/draive/multimodal/__init__.py
  • src/draive/helpers/instruction_preparation.py
  • src/draive/postgres/templates.py
  • src/draive/generation/audio/state.py
  • src/draive/conversation/realtime/types.py
  • src/draive/openai/responses.py
  • src/draive/openai/images.py
  • src/draive/postgres/memory.py
  • src/draive/generation/model/state.py
  • src/draive/generation/text/types.py
  • src/draive/generation/text/state.py
  • src/draive/multimodal/templates/types.py
  • src/draive/conversation/realtime/default.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/generation/model/default.py
  • src/draive/generation/image/default.py
  • src/draive/generation/audio/types.py
  • src/draive/generation/text/default.py
  • src/draive/multimodal/templates/variables.py
  • src/draive/conversation/completion/default.py
  • src/draive/conversation/completion/state.py
  • src/draive/conversation/realtime/state.py
  • src/draive/helpers/instruction_refinement.py
  • src/draive/postgres/__init__.py
  • src/draive/conversation/completion/types.py
  • src/draive/generation/audio/default.py
  • src/draive/generation/image/state.py
  • src/draive/__init__.py
  • src/draive/models/generative.py
  • src/draive/stages/stage.py
  • src/draive/generation/image/types.py
  • src/draive/multimodal/templates/repository.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.py: Do not perform real network I/O in unit tests; mock providers/HTTP
Keep tests fast and focused on changed code; start with unit tests around new types/functions/adapters
Use fixtures from tests/ or add focused ones; avoid heavy integration scaffolding
Use pytest-asyncio for coroutine tests (@pytest.mark.asyncio)
Prefer scoping with ctx.scope(...) in async tests and bind required State instances explicitly
Avoid real I/O and network in async tests; stub provider calls and HTTP

Files:

  • tests/test_generative_merge.py
  • tests/test_multimodal_template_variables.py
  • tests/test_toolbox.py
src/draive/multimodal/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement content types and helpers (MultimodalContent, TextContent, ArtifactContent) under draive/multimodal/

Files:

  • src/draive/multimodal/__init__.py
  • src/draive/multimodal/templates/types.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/multimodal/templates/variables.py
  • src/draive/multimodal/templates/repository.py
docs/**/*

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*: When behavior/API changes, update relevant docs under docs/ and examples as applicable
When adding public APIs, update examples/guides and ensure cross-links render

Files:

  • docs/guides/Templates.md
  • docs/guides/ComprehensiveEvaluation.md
  • docs/guides/Postgres.md
src/draive/{httpx,mcp,postgres,opentelemetry}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Place integrations under draive/httpx, draive/mcp, draive/postgres, draive/opentelemetry

Files:

  • src/draive/postgres/templates.py
  • src/draive/postgres/memory.py
  • src/draive/postgres/__init__.py
src/draive/conversation/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement higher-level chat/realtime conversations under draive/conversation/

Files:

  • src/draive/conversation/realtime/types.py
  • src/draive/conversation/realtime/default.py
  • src/draive/conversation/completion/default.py
  • src/draive/conversation/completion/state.py
  • src/draive/conversation/realtime/state.py
  • src/draive/conversation/completion/types.py
mkdocs.yml

📄 CodeRabbit inference engine (AGENTS.md)

Register documentation navigation updates in mkdocs.yml (nav section) when adding/adjusting pages

Files:

  • mkdocs.yml
src/draive/{openai,anthropic,mistral,gemini,vllm,ollama,bedrock,cohere}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/{openai,anthropic,mistral,gemini,vllm,ollama,bedrock,cohere}/**/*.py: Provider-specific feature modules live under their respective provider directories
Translate provider/SDK errors into typed exceptions; do not raise bare Exception and preserve context
Use environment variables for credentials and resolve via helper functions like getenv_str

Files:

  • src/draive/openai/responses.py
  • src/draive/openai/images.py
{pyproject.toml,pyrightconfig.json}

📄 CodeRabbit inference engine (AGENTS.md)

Use Ruff, Bandit, and Pyright (strict) via make lint

Files:

  • pyproject.toml
src/draive/__init__.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/__init__.py: Centralize public exports in src/draive/init.py
Update src/draive/init.py exports when API surface changes

Files:

  • src/draive/__init__.py
src/draive/models/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Keep core abstractions (GenerativeModel, tools, instructions) under draive/models/

Files:

  • src/draive/models/generative.py
src/draive/stages/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement pipeline stage abstractions and helpers under draive/stages/

Files:

  • src/draive/stages/stage.py
🧠 Learnings (6)
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/__init__.py : Centralize public exports in src/draive/__init__.py

Applied to files:

  • src/draive/multimodal/__init__.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/multimodal/**/*.py : Implement content types and helpers (MultimodalContent, TextContent, ArtifactContent) under draive/multimodal/

Applied to files:

  • src/draive/multimodal/__init__.py
  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/**/*.py : Import Haiway symbols directly (from haiway import State, ctx)

Applied to files:

  • src/draive/helpers/instruction_preparation.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/**/*.py : Add NumPy-style docstrings for public symbols with Parameters/Returns/Raises and rationale when non-obvious

Applied to files:

  • src/draive/multimodal/templates/types.py
  • src/draive/multimodal/templates/repository.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/__init__.py : Update src/draive/__init__.py exports when API surface changes

Applied to files:

  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/models/**/*.py : Keep core abstractions (GenerativeModel, tools, instructions) under draive/models/

Applied to files:

  • src/draive/__init__.py
  • src/draive/stages/stage.py
🧬 Code graph analysis (34)
src/draive/generation/model/types.py (3)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
tests/test_generative_merge.py (1)
src/draive/models/types.py (2)
  • ModelReasoning (518-562)
  • ModelToolRequest (311-353)
src/draive/multimodal/__init__.py (2)
src/draive/multimodal/templates/repository.py (4)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
  • TemplatesRepository (61-306)
src/draive/multimodal/templates/types.py (6)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateDefining (123-132)
  • TemplateListing (105-109)
  • TemplateLoading (113-119)
  • TemplateMissing (18-27)
src/draive/helpers/instruction_preparation.py (1)
src/draive/multimodal/templates/types.py (1)
  • TemplateDeclaration (80-101)
src/draive/postgres/templates.py (2)
src/draive/multimodal/templates/repository.py (11)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
  • TemplatesRepository (61-306)
  • listing (313-317)
  • listing (384-392)
  • load (247-262)
  • loading (319-325)
  • loading (394-404)
  • defining (327-342)
  • defining (406-425)
src/draive/multimodal/templates/types.py (1)
  • TemplateDeclaration (80-101)
src/draive/generation/audio/state.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
tests/test_multimodal_template_variables.py (3)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/variables.py (3)
  • parse_template_variables (14-21)
  • resolve_multimodal_template (48-67)
  • resolve_text_template (24-45)
src/draive/multimodal/text.py (1)
  • TextContent (11-82)
src/draive/conversation/realtime/types.py (2)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/openai/responses.py (1)
src/draive/models/types.py (1)
  • ModelReasoning (518-562)
src/draive/openai/images.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-306)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/postgres/memory.py (1)
src/draive/models/types.py (3)
  • ModelInput (429-501)
  • ModelMemoryRecall (719-766)
  • ModelOutput (573-672)
src/draive/generation/model/state.py (3)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/text/types.py (3)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/text/state.py (2)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/types.py (2)
tests/test_toolbox.py (4)
  • meta (109-110)
  • meta (310-311)
  • description (97-98)
  • description (298-299)
src/draive/stages/stage.py (1)
  • with_meta (1390-1435)
src/draive/conversation/realtime/default.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-306)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/multimodal/templates/__init__.py (2)
src/draive/multimodal/templates/repository.py (4)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
  • TemplatesRepository (61-306)
src/draive/multimodal/templates/types.py (6)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateDefining (123-132)
  • TemplateListing (105-109)
  • TemplateLoading (113-119)
  • TemplateMissing (18-27)
src/draive/generation/model/default.py (4)
src/draive/multimodal/templates/types.py (2)
  • Template (31-76)
  • with_arguments (51-62)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/parameters/model.py (2)
  • json_schema (443-451)
  • simplified_schema (433-440)
src/draive/parameters/schema.py (1)
  • simplified_schema (9-26)
src/draive/generation/image/default.py (4)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-306)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/openai/images.py (1)
  • generate_image (21-60)
src/draive/models/generative.py (6)
  • GenerativeModel (45-515)
  • completion (62-71)
  • completion (74-83)
  • completion (87-96)
  • completion (99-108)
  • completion (111-181)
src/draive/generation/audio/types.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/text/default.py (3)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/models/generative.py (6)
  • GenerativeModel (45-515)
  • loop (185-194)
  • loop (197-206)
  • loop (210-219)
  • loop (222-231)
  • loop (234-306)
src/draive/multimodal/templates/variables.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (2)
  • of (33-45)
  • of (82-96)
src/draive/conversation/completion/default.py (4)
src/draive/models/types.py (2)
  • ModelMemoryRecall (719-766)
  • ModelOutput (573-672)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/models/generative.py (5)
  • loop (185-194)
  • loop (197-206)
  • loop (210-219)
  • loop (222-231)
  • loop (234-306)
src/draive/conversation/completion/state.py (4)
src/draive/models/types.py (2)
  • ModelMemoryRecall (719-766)
  • ModelOutput (573-672)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/conversation/realtime/state.py (4)
src/draive/models/types.py (2)
  • ModelMemoryRecall (719-766)
  • ModelOutput (573-672)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/helpers/instruction_refinement.py (4)
src/draive/multimodal/content.py (2)
  • MultimodalContent (25-592)
  • MultimodalTag (599-700)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-306)
  • loading (319-325)
  • loading (394-404)
  • load (247-262)
src/draive/multimodal/text.py (1)
  • TextContent (11-82)
src/draive/postgres/__init__.py (2)
src/draive/multimodal/templates/repository.py (3)
  • templates (105-108)
  • templates (111-114)
  • templates (117-121)
src/draive/postgres/templates.py (1)
  • PostgresTemplatesRepository (28-173)
src/draive/conversation/completion/types.py (2)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/generation/audio/default.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/generation/image/state.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/__init__.py (2)
src/draive/multimodal/templates/types.py (3)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateMissing (18-27)
src/draive/multimodal/templates/repository.py (1)
  • TemplatesRepository (61-306)
src/draive/stages/stage.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (185-193)
  • resolve_str (196-204)
  • resolve_str (207-244)
src/draive/generation/image/types.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (31-76)
src/draive/multimodal/templates/repository.py (4)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (8)
  • Template (31-76)
  • TemplateDeclaration (80-101)
  • TemplateDefining (123-132)
  • TemplateListing (105-109)
  • TemplateLoading (113-119)
  • TemplateMissing (18-27)
  • of (33-45)
  • of (82-96)
src/draive/multimodal/templates/variables.py (3)
  • parse_template_variables (14-21)
  • resolve_multimodal_template (48-67)
  • resolve_text_template (24-45)
src/draive/postgres/templates.py (3)
  • listing (51-82)
  • defining (128-166)
  • load (88-119)
🪛 LanguageTool
docs/guides/Templates.md

[style] ~104-~104: It’s more common nowadays to write this noun as one word.
Context: ...fining templates programmatically, pass variables={"user": "User name"} to document expected arguments. This...

(RECOMMENDED_COMPOUNDS)

🔇 Additional comments (35)
Makefile (2)

9-10: Inconsistency with AI summary: .env inclusion is still present.

The AI summary states ".env inclusion" was removed, but lines 9–10 show the -include .env directive and its comment are still present in the code. Please clarify whether this change was intended to be removed or if the summary reflects an abandoned change.


13-13: UV version bump looks good.

The patch/minor version bump from 0.9.0 to 0.9.4 is straightforward and safe. The version-checking logic in the uv_check target (lines 33–45) correctly handles version comparisons, so this update will integrate seamlessly.

src/draive/postgres/memory.py (1)

9-15: LGTM! Import addition is correct and necessary.

The ModelOutput import is properly added and used at line 275 in the _decode_context function. The multi-line import format is consistent with the existing style.

mkdocs.yml (1)

101-101: LGTM!

The Templates guide is correctly added to the documentation navigation.

pyproject.toml (1)

8-8: LGTM!

The version bump to 0.90.0 is appropriate for the introduction of the new Templates subsystem.

src/draive/postgres/__init__.py (1)

12-12: LGTM!

The import and export of PostgresTemplatesRepository correctly replaces the previous Instructions-based repository.

src/draive/generation/audio/types.py (1)

3-15: LGTM!

The protocol signature correctly accepts both Template and ModelInstructions, aligning with the new Templates subsystem while maintaining compatibility.

tests/test_toolbox.py (1)

14-14: LGTM!

The import path consolidation from draive.models.types to draive.models correctly reflects the module reorganization.

src/draive/openai/responses.py (1)

84-84: LGTM! Import path consolidated.

The import path for ModelReasoning has been updated to use the parent module, aligning with the public API surface consolidation across the codebase.

tests/test_generative_merge.py (1)

1-1: LGTM! Test imports aligned with new public API.

Import path updated to match the consolidated public API surface. Test logic remains unchanged.

src/draive/generation/model/state.py (2)

8-9: Public API evolution to support Templates.

Imports updated to introduce Template alongside ModelInstructions, replacing the previous ResolveableInstructions type as part of the Templates subsystem rollout.


23-23: Signature expanded to accept Template | ModelInstructions.

All overloads and the statemethod now accept Template | ModelInstructions, broadening the API to support template-based instruction resolution while maintaining backward compatibility with direct string instructions.

Also applies to: 38-38, 53-53

docs/guides/ComprehensiveEvaluation.md (1)

3-5: LGTM! Formatting refinements only.

Text rewrapping for consistency with no semantic changes.

Also applies to: 9-14, 66-67, 282-283

src/draive/openai/images.py (2)

8-9: Imports updated for Templates subsystem.

Replaced InstructionsRepository and legacy instruction types with Template, TemplatesRepository, and ModelInstructions to support the new template-based resolution flow.


24-24: Resolution mechanism migrated to TemplatesRepository.

The signature now accepts Template | ModelInstructions, and resolution is performed via TemplatesRepository.resolve_str, which correctly handles both template references and direct string instructions.

Also applies to: 36-36

src/draive/generation/image/types.py (2)

3-4: Protocol imports aligned with Templates subsystem.

Replaced ResolveableInstructions with ModelInstructions and added Template to support the new instruction handling approach.


15-15: Protocol signature broadened to Template | ModelInstructions.

The ImageGenerating protocol now accepts the expanded instruction types, maintaining compatibility with implementations that use the new template resolution flow.

src/draive/multimodal/__init__.py (1)

8-16: Public API surface expanded with Templates subsystem.

The Template family (Template, TemplateDeclaration, TemplatesRepository, etc.) is now exposed from draive.multimodal, making template-based content resolution available to consumers. This aligns with the goal of centralizing public exports.

Based on learnings

Also applies to: 25-31

src/draive/generation/model/types.py (2)

4-5: Protocol imports migrated to Templates surface.

Imports updated to replace ResolveableInstructions with ModelInstructions and add Template, supporting the template-based instruction handling approach.


29-29: Protocol contract expanded for Template support.

The ModelGenerating protocol now accepts Template | ModelInstructions, enabling implementations to leverage template resolution while maintaining compatibility with direct string instructions.

src/draive/conversation/realtime/types.py (1)

12-13: LGTM — Type union aligns with Template-based refactor.

The signature change to Template | ModelInstructions is consistent with the broader migration away from ResolveableInstructions. Imports are correct.

Also applies to: 130-130

src/draive/multimodal/templates/__init__.py (1)

1-19: LGTM — Public exports are complete and follow conventions.

All Template-related types and the repository are correctly imported and included in __all__. Aligns with the learning about centralizing public exports in __init__.py.

Based on learnings

src/draive/generation/audio/default.py (1)

8-11: LGTM — Template resolution integrated correctly.

The migration from InstructionsRepository to TemplatesRepository.resolve_str is correct, and the Template | ModelInstructions type union aligns with the broader refactor. The async call is properly awaited.

Also applies to: 19-19, 25-25

tests/test_multimodal_template_variables.py (1)

1-207: LGTM — Comprehensive test coverage for template variables.

The tests thoroughly cover parsing (unique names, adjacent placeholders, symbolic names, edge cases) and resolution (text and multimodal, missing/unused arguments, metadata preservation). All keyword-only arguments are correctly passed as arguments=....

src/draive/generation/text/default.py (1)

9-9: LGTM — Template resolution follows established pattern.

The migration mirrors the audio generation module: correct imports, consistent Template | ModelInstructions typing, and proper async resolution via TemplatesRepository.resolve_str.

Also applies to: 13-13, 20-20, 28-28

src/draive/generation/text/types.py (1)

4-5: LGTM — Protocol type updated correctly.

The Template | ModelInstructions union is consistent with the migration. Imports are correct.

Also applies to: 15-15

src/draive/conversation/realtime/state.py (1)

40-51: Type update to Template | ModelInstructions looks good.

Signature and docstring align with the new template-based flow.

src/draive/generation/text/state.py (1)

20-25: Consistent typing migration acknowledged.

Overloads and impl now accept Template | ModelInstructions; wrapper composes inputs and delegates. No further changes needed here.

If generate_text resolves via TemplatesRepository.resolve_str, we’re good. Otherwise, confirm resolution is centralized there.

Also applies to: 31-36, 42-48

src/draive/generation/image/state.py (1)

20-23: Typing migration LGTM.

Wrappers now accept Template | ModelInstructions and delegate correctly.

Also applies to: 29-33, 38-41

src/draive/conversation/completion/types.py (1)

8-9: LGTM! Type signatures correctly updated to Template | ModelInstructions.

The parameter type changes across all overloads and the main signature are consistent with the broader Template-based refactor. The import updates align with the new multimodal Template API.

Also applies to: 26-26, 38-38, 49-49

src/draive/conversation/completion/state.py (1)

11-11: LGTM! Conversation.completion correctly updated to use Template | ModelInstructions.

The type signature changes across all overloads, main implementation, and docstring are consistent with the Template-based API surface.

Also applies to: 18-18, 37-37, 50-50, 62-62, 73-73

src/draive/stages/stage.py (1)

39-39: LGTM! Stage methods consistently updated to Template-based API.

All completion-related stage methods (completion, prompting_completion, loopback_completion, result_completion) and the internal _model_routing function correctly:

  • Accept Template | ModelInstructions parameters
  • Resolve templates via await TemplatesRepository.resolve_str(instructions) before passing to GenerativeModel.loop

The refactor is thorough and consistent across the module.

Also applies to: 49-49, 293-293, 313-313, 356-356, 380-380, 401-401, 447-447, 469-469, 484-484, 537-537, 559-559, 573-573, 606-606, 2054-2054, 2075-2075

src/draive/helpers/instruction_refinement.py (1)

13-20: LGTM! Instruction refinement correctly migrated to Template-based API.

The refactor consistently updates:

  • Data types from Instructions to Template
  • Repository from InstructionsRepository to TemplatesRepository
  • Identifier-based loading (line 144) instead of name-based
  • Loading pathway via TemplatesRepository.load (line 246)

The patched repository pattern (lines 135-158) correctly intercepts template loading to provide refined instruction content during evaluation.

Also applies to: 28-28, 105-105, 135-158, 246-246

src/draive/__init__.py (1)

178-181: LGTM! Public API correctly updated to expose Template-based surface.

The top-level exports now include:

  • Template, TemplateDeclaration, TemplateMissing, TemplatesRepository from draive.multimodal
  • Removed Instructions-related symbols

This aligns with the repository-wide migration from Instructions to Templates subsystem.

Based on learnings.

Also applies to: 350-353

src/draive/multimodal/templates/repository.py (1)

1-32: LGTM!

Imports follow coding guidelines with direct haiway imports and appropriate module structure.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/draive/models/generative.py (2)

620-642: Inconsistency between parameter type and docstring.

The parameter instructions is typed as ModelInstructions (lines 620, 631, 642), but the docstring at line 656 claims Template | ModelInstructions. Update the parameter type to match the docstring or vice versa for consistency with other methods in the PR.

Apply this diff to align the signature with the docstring:

     @overload
     @classmethod
     async def session(
         cls,
         *,
-        instructions: ModelInstructions = "",
+        instructions: Template | ModelInstructions = "",
         memory: ModelMemory | ModelContext = (),
         tools: ModelToolsDeclaration = ModelToolsDeclaration.none,
         output: ModelSessionOutputSelection = "auto",
         **extra: Any,
     ) -> ModelSessionScope: ...

     @overload
     async def session(
         self,
         *,
-        instructions: ModelInstructions = "",
+        instructions: Template | ModelInstructions = "",
         memory: ModelMemory | ModelContext = (),
         tools: ModelToolsDeclaration = ModelToolsDeclaration.none,
         output: ModelSessionOutputSelection = "auto",
         **extra: Any,
     ) -> ModelSessionScope: ...

     @statemethod
     async def session(
         self,
         *,
-        instructions: ModelInstructions = "",
+        instructions: Template | ModelInstructions = "",
         memory: ModelMemory | ModelContext = (),
         tools: ModelToolsDeclaration = ModelToolsDeclaration.none,
         output: ModelSessionOutputSelection = "auto",
         **extra: Any,
     ) -> ModelSessionScope:

648-678: <!-- [scratchpad_start]
[task verification_1/1 conclusion_confirmation]
[observations]

  • ModelSessionPreparing protocol (types.py:1100): instructions : ModelInstructions (NOT Template)
  • session() method signature (generative.py:656): instructions : Template | ModelInstructions
  • session() implementation (generative.py:692-694): direct pass-through without resolution
  • Docstring (line 650): "Resolves instructions (via InstructionsRepository)" - FALSE
  • Pattern throughout codebase: Callers use await TemplatesRepository.resolve_str(instructions) BEFORE calling GenerativeModel methods
  • Examples: stages/stage.py, generation/*.py, conversation/realtime/default.py (line 52), conversation/completion/default.py (line 97-98)

[analysis]

  • Review comment is CORRECT: docstring falsely claims resolution
  • The real issue: signature accepts Template but protocol expects ModelInstructions
  • Current implementation: passes instructions unresolved to provider
  • Expected pattern: callers should resolve before calling session()
  • This is a design inconsistency or incomplete implementation

[conclusion]
The review comment accurately identifies that the docstring is wrong. The code doesn't resolve instructions. However, the issue is even deeper:

  1. Docstring should be corrected (no resolution happens)
  2. Method should either resolve instructions itself OR not accept Template union type
  3. Looking at the pattern, callers are responsible for resolution
    [done]
    [scratchpad_start] -->

Docstring is outdated—session() does not resolve instructions.

The docstring claims "Resolves instructions (via InstructionsRepository)" but the implementation at line 692–699 passes instructions directly to session_preparing without any transformation. The protocol definition expects ModelInstructions (not Template), and the codebase pattern shows that resolution is the caller's responsibility—see conversation/realtime/default.py:52 and other callers using await TemplatesRepository.resolve_str(instructions) before invoking this method.

Update the docstring to remove the false resolution claim, or alternatively, implement instruction resolution in the method body to match the Template | ModelInstructions parameter type.

src/draive/helpers/instruction_refinement.py (1)

27-100: Update docstring to document conditional TemplatesRepository context requirement.

The function uses @statemethod patterns correctly, but the docstring omits an important constraint: TemplatesRepository must be active in context when instructions_content is None (line 246). If instructions_content is provided, no context is required. This conditional dependency should be documented to prevent runtime errors for callers.

Update the docstring (lines 43–59) to clarify:

  • TemplatesRepository dependency and when it applies (only if instructions_content is None)
  • Suggest pairing with instructions_content parameter when context setup is difficult
♻️ Duplicate comments (20)
src/draive/multimodal/templates/variables.py (2)

7-11: Fix __all__: duplicate entry still present.

The duplicate "resolve_multimodal_template" entry remains, and "resolve_text_template" is missing despite being a public function used by repository.py.

Apply this diff:

 __all__ = (
     "parse_template_variables",
-    "resolve_multimodal_template",
-    "resolve_multimodal_template",
+    "resolve_text_template",
+    "resolve_multimodal_template",
 )

1-1: Use built-in set[str] instead of Set from collections.abc.

Python 3.12+ supports built-in generic syntax; remove the Set import and update all annotations to use set[str].

Apply this diff:

-from collections.abc import Generator, Mapping, MutableSequence, Set
+from collections.abc import Generator, Mapping, MutableSequence

Then update line 16:

 def parse_template_variables(
     template: str,
-) -> Set[str]:
+) -> set[str]:

As per coding guidelines.

src/draive/helpers/instruction_preparation.py (2)

37-41: Prefer TemplateDeclaration.of(...) for consistency.

Direct dataclass construction bypasses the factory method. Use .of() to match State construction patterns across the codebase.

Apply this diff:

-                instruction_declaration = TemplateDeclaration(
-                    identifier="instruction",
+                instruction_declaration = TemplateDeclaration.of(
+                    identifier="instruction",
                     description=description,
                     variables={},
                 )

As per coding guidelines.


25-30: Add NumPy-style docstring to public helper.

prepare_instructions is a public function but lacks documentation explaining its behavior, particularly the ambiguity handling via InstructionPreparationAmbiguity.

Example docstring:

async def prepare_instructions(
    instruction: TemplateDeclaration | str,
    /,
    *,
    guidelines: str | None = None,
) -> ModelInstructions:
    """Prepare precise model instructions from a task description or declaration.
    
    Parameters
    ----------
    instruction
        Either a template declaration (with variables and description) or a raw task description string.
    guidelines
        Optional guidance appended to the preparation prompt.
    
    Returns
    -------
    ModelInstructions
        Prepared instruction string ready for model consumption.
    
    Raises
    ------
    InstructionPreparationAmbiguity
        When the task requires clarification questions.
    ValueError
        When instruction preparation fails unexpectedly.
    """

As per coding guidelines.

src/draive/multimodal/templates/types.py (2)

109-113: Consider using Map() for immutability consistency.

Line 111 constructs a plain dict while other State initializers use Map() for immutable mappings. Using Map() would align with Haiway's immutability patterns.

Apply this diff if Map is preferred:

         return self.__class__(
             identifier=self.identifier,
-            arguments={**self.arguments, **arguments},
+            arguments=Map({**self.arguments, **arguments}),
             meta=self.meta,
         )

Note: Verify Map is imported from haiway if applying this change.


189-194: Consider using Map() for variables default.

Line 192 uses a plain {} while other immutable State attributes typically use Map(). This improves consistency with Haiway's immutability patterns.

Apply this diff if Map is preferred:

         return cls(
             identifier=identifier,
             description=description,
-            variables=variables if variables is not None else {},
+            variables=variables if variables is not None else Map(),
             meta=Meta.of(meta),
         )

Note: Ensure Map is imported from haiway if applying this change.

src/draive/multimodal/templates/repository.py (5)

578-580: Critical: Use ctx.log_warn instead of ctx.log_warning.

Haiway's logging API provides ctx.log_debug/info/warn/error. The method ctx.log_warning does not exist and will raise AttributeError at runtime.

Apply this diff:

                             case _:  # skip with warning
-                                ctx.log_warning(
+                                ctx.log_warn(
                                     "Invalid templates file storage element, skipping..."
                                 )

As per coding guidelines.


433-437: Return templates in deterministic order.

Non-deterministic dict ordering can cause flaky tests and inconsistent behavior. Sort declarations by identifier for reproducibility.

Apply this diff:

     async def listing(
         self,
         **extra: Any,
     ) -> Sequence[TemplateDeclaration]:
-        return tuple(self._declarations.values())
+        return tuple(sorted(self._declarations.values(), key=lambda d: d.identifier))

508-512: Return templates in deterministic order.

Same issue as VolatileStorage: dict value order is non-deterministic. Sort for stable, reproducible results.

Apply this diff:

         assert self._declarations is not None  # nosec: B101
-        return tuple(self._declarations.values())
+        return tuple(sorted(self._declarations.values(), key=lambda d: d.identifier))

607-618: Write templates in deterministic order for reproducible files.

Non-deterministic dict iteration causes file content to vary across runs, breaking reproducibility and complicating version control diffs.

Apply this diff:

-        file_contents: bytes = json.dumps(
+        ordered = sorted(self._declarations.values(), key=lambda d: d.identifier)
+        file_contents: bytes = json.dumps(
             [
                 {
-                    "identifier": declaration.identifier,
-                    "description": declaration.description,
-                    "variables": declaration.variables,
-                    "content": self._contents[declaration.identifier],
-                    "meta": declaration.meta.to_mapping(),
+                    "identifier": d.identifier,
+                    "description": d.description,
+                    "variables": d.variables,
+                    "content": self._contents[d.identifier],
+                    "meta": d.meta.to_mapping(),
                 }
-                for declaration in self._declarations.values()
-            ]
+                for d in ordered
+            ],
+            sort_keys=True,
         ).encode()

Note: Adding sort_keys=True ensures nested dict keys are also stable.


464-471: Rename parameter name to identifier for consistency.

The codebase consistently uses identifier for template names (see listing, loading, defining methods). Using name here breaks that convention.

Apply this diff:

     async def removing(
         self,
-        name: str,
+        identifier: str,
         meta: Meta,
         **extra: Any,
     ) -> None:
-        del self._declarations[name]
-        del self._contents[name]
+        del self._declarations[identifier]
+        del self._contents[identifier]
docs/guides/Templates.md (1)

1-122: Excellent documentation structure and coverage.

The guide provides clear explanations of Template basics, storage backends, runtime resolution, and migration paths. Code examples are well-chosen and demonstrate key patterns effectively.

Note: The minor style suggestion about "User name" vs "username" at line 104 has already been flagged in a previous review.

src/draive/generation/text/types.py (1)

4-20: Protocol signature correctly updated for Template support.

The TextGenerating protocol now accepts Template | ModelInstructions and includes **extra for extensibility, consistent with the broader migration.

Note: A previous review suggested adding a docstring for API reference purposes, which remains a minor optional improvement.

src/draive/generation/image/default.py (1)

25-25: Forward **extra to template resolution for backend context.

As flagged in a previous review, TemplatesRepository.resolve_str accepts **extra to allow backends to receive contextual parameters. The function forwards **extra to GenerativeModel.completion but not to template resolution.

Apply this diff:

-            instructions=await TemplatesRepository.resolve_str(instructions),
+            instructions=await TemplatesRepository.resolve_str(instructions, **extra),
docs/guides/Postgres.md (1)

88-88: API naming: use resolve_str, not resolve_as_text.

The documentation references resolve_as_text, but the actual API method is resolve_str. Update for consistency.

Apply this diff:

-- `resolve(template)` and `resolve_as_text(template)` reuse a cached loader keyed by identifier to
+- `resolve(template)` and `resolve_str(template)` reuse a cached loader keyed by identifier to
src/draive/generation/audio/state.py (1)

20-20: Type-default mismatch: add | str to union.

The instructions parameter defaults to "" (a str), but the union Template | ModelInstructions does not include str. This creates a type inconsistency across all three signatures.

Apply this diff to all three locations:

-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",

Also applies to: 29-29, 38-38

src/draive/generation/model/default.py (1)

36-62: Forward **extra and consider unifying resolution paths.

The function receives **extra (line 30) but does not forward it to TemplatesRepository.resolve_str in any branch. Additionally, the string and Template resolution paths diverge (str.format vs. resolve_str), which creates inconsistent placeholder semantics.

Apply this diff to forward **extra and optionally unify the paths:

             case "full":
                 if isinstance(instructions, str):
                     resolved_instructions = instructions.format(
                         model_schema=generated.json_schema(indent=2),
                     )

                 else:
                     resolved_instructions = await TemplatesRepository.resolve_str(
                         instructions.with_arguments(
                             model_schema=generated.json_schema(indent=2),
-                        )
+                        ),
+                        **extra,
                     )

             case "simplified":
                 if isinstance(instructions, str):
                     resolved_instructions = instructions.format(
                         model_schema=generated.simplified_schema(indent=2),
                     )

                 else:
                     resolved_instructions = await TemplatesRepository.resolve_str(
                         instructions.with_arguments(
                             model_schema=generated.simplified_schema(indent=2),
-                        )
+                        ),
+                        **extra,
                     )

             case "skip":  # instruction is not modified
-                resolved_instructions = await TemplatesRepository.resolve_str(instructions)
+                resolved_instructions = await TemplatesRepository.resolve_str(
+                    instructions,
+                    **extra,
+                )
src/draive/postgres/templates.py (1)

74-82: Prefer TemplateDeclaration.of for State construction.

Direct constructor calls bypass any validation or normalization logic in the .of() helper. Use the classmethod for consistency and future-proofing.

Apply this diff:

         return tuple(
-            TemplateDeclaration(
+            TemplateDeclaration.of(
                 identifier=cast(str, result["identifier"]),
                 description=cast(str | None, result["description"]),
                 variables=json.loads(cast(str, result["variables"] or "{}")),
                 meta=Meta.from_json(cast(str, result["meta"] or "{}")),
             )
             for result in results
         )
src/draive/conversation/realtime/default.py (1)

52-52: Forward **extra to TemplatesRepository.resolve_str.

The function receives **extra (line 43) but does not pass it to the repository call. Backends such as Postgres may require this context for template resolution.

Apply this diff:

-        instructions=await TemplatesRepository.resolve_str(instructions),
+        instructions=await TemplatesRepository.resolve_str(instructions, **extra),
src/draive/conversation/completion/default.py (1)

97-105: Use to_str() for multimodal values and build the mapping once.

The current code converts all non-string values using str(value), but multimodal values should prefer value.to_str() when available. Also, the mapping is built inline; consider building it once in a local variable for reuse and clarity.

Apply this diff:

+        args = (
+            {
+                key: (
+                    value
+                    if isinstance(value, str)
+                    else (value.to_str() if hasattr(value, "to_str") else str(value))
+                )
+                for key, value in memory_recall.variables.items()
+            }
+            if memory_recall.variables
+            else None
+        )
+
         resolved_instructions: str = await TemplatesRepository.resolve_str(
             instructions,
-            arguments={
-                key: value if isinstance(value, str) else str(value)
-                for key, value in memory_recall.variables.items()
-            }
-            if memory_recall.variables
-            else None,
+            arguments=args,
         )

Also applies to lines 150-158.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 031f8d4 and 92beca4.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (48)
  • Makefile (1 hunks)
  • docs/guides/ComprehensiveEvaluation.md (3 hunks)
  • docs/guides/Postgres.md (3 hunks)
  • docs/guides/Templates.md (1 hunks)
  • mkdocs.yml (1 hunks)
  • pyproject.toml (1 hunks)
  • src/draive/__init__.py (2 hunks)
  • src/draive/conversation/completion/default.py (7 hunks)
  • src/draive/conversation/completion/state.py (5 hunks)
  • src/draive/conversation/completion/types.py (4 hunks)
  • src/draive/conversation/realtime/default.py (3 hunks)
  • src/draive/conversation/realtime/state.py (3 hunks)
  • src/draive/conversation/realtime/types.py (2 hunks)
  • src/draive/generation/audio/default.py (1 hunks)
  • src/draive/generation/audio/state.py (4 hunks)
  • src/draive/generation/audio/types.py (2 hunks)
  • src/draive/generation/image/default.py (1 hunks)
  • src/draive/generation/image/state.py (4 hunks)
  • src/draive/generation/image/types.py (2 hunks)
  • src/draive/generation/model/default.py (3 hunks)
  • src/draive/generation/model/state.py (4 hunks)
  • src/draive/generation/model/types.py (2 hunks)
  • src/draive/generation/text/default.py (1 hunks)
  • src/draive/generation/text/state.py (4 hunks)
  • src/draive/generation/text/types.py (2 hunks)
  • src/draive/helpers/instruction_preparation.py (3 hunks)
  • src/draive/helpers/instruction_refinement.py (5 hunks)
  • src/draive/models/__init__.py (0 hunks)
  • src/draive/models/generative.py (5 hunks)
  • src/draive/models/instructions/__init__.py (0 hunks)
  • src/draive/models/instructions/repository.py (0 hunks)
  • src/draive/models/instructions/template.py (0 hunks)
  • src/draive/models/instructions/types.py (0 hunks)
  • src/draive/multimodal/__init__.py (2 hunks)
  • src/draive/multimodal/templates/__init__.py (1 hunks)
  • src/draive/multimodal/templates/repository.py (1 hunks)
  • src/draive/multimodal/templates/types.py (1 hunks)
  • src/draive/multimodal/templates/variables.py (1 hunks)
  • src/draive/openai/images.py (3 hunks)
  • src/draive/openai/responses.py (1 hunks)
  • src/draive/postgres/__init__.py (2 hunks)
  • src/draive/postgres/instructions.py (0 hunks)
  • src/draive/postgres/memory.py (1 hunks)
  • src/draive/postgres/templates.py (1 hunks)
  • src/draive/stages/stage.py (15 hunks)
  • tests/test_generative_merge.py (1 hunks)
  • tests/test_multimodal_template_variables.py (1 hunks)
  • tests/test_toolbox.py (1 hunks)
💤 Files with no reviewable changes (6)
  • src/draive/models/instructions/repository.py
  • src/draive/postgres/instructions.py
  • src/draive/models/instructions/template.py
  • src/draive/models/init.py
  • src/draive/models/instructions/types.py
  • src/draive/models/instructions/init.py
🧰 Additional context used
📓 Path-based instructions (13)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use Python 3.12+ features and syntax across the codebase
Format code exclusively with Ruff (make format); do not use other formatters
Skip module-level docstrings

Files:

  • src/draive/multimodal/templates/variables.py
  • src/draive/generation/audio/default.py
  • src/draive/postgres/__init__.py
  • tests/test_multimodal_template_variables.py
  • src/draive/openai/responses.py
  • src/draive/generation/text/default.py
  • src/draive/multimodal/templates/types.py
  • src/draive/generation/audio/types.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/helpers/instruction_refinement.py
  • src/draive/multimodal/templates/repository.py
  • src/draive/generation/text/types.py
  • src/draive/generation/image/state.py
  • src/draive/conversation/completion/types.py
  • tests/test_toolbox.py
  • src/draive/postgres/memory.py
  • src/draive/generation/image/default.py
  • src/draive/openai/images.py
  • src/draive/generation/model/state.py
  • src/draive/conversation/realtime/types.py
  • src/draive/__init__.py
  • src/draive/postgres/templates.py
  • src/draive/conversation/completion/state.py
  • src/draive/conversation/completion/default.py
  • src/draive/models/generative.py
  • src/draive/generation/model/default.py
  • src/draive/generation/image/types.py
  • src/draive/stages/stage.py
  • src/draive/generation/text/state.py
  • src/draive/generation/audio/state.py
  • tests/test_generative_merge.py
  • src/draive/helpers/instruction_preparation.py
  • src/draive/generation/model/types.py
  • src/draive/conversation/realtime/default.py
  • src/draive/conversation/realtime/state.py
  • src/draive/multimodal/__init__.py
src/draive/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/**/*.py: Import Haiway symbols directly (from haiway import State, ctx)
Use ctx.scope(...) to bind scoped Disposables and active State; avoid global state
Route all logs through ctx.log_debug/info/warn/error; do not use print
Use latest, most strict typing syntax (Python 3.12+), with strict typing only for public APIs
Avoid loose Any except at explicit third‑party boundaries
Prefer explicit attribute access with static types; avoid dynamic getattr except at narrow boundaries
Prefer Mapping/Sequence/Iterable in public types over dict/list/set
Use final where applicable; avoid inheritance and prefer composition
Use precise unions (|) and narrow with match/isinstance; avoid cast unless provably safe and localized
Model immutable data/config and facades with haiway.State; provide ergonomic classmethods like .of(...)
Avoid in-place mutation; use State.updated(...) or functional builders to produce new instances
Access active state via haiway.ctx inside async scopes (ctx.scope(...))
Use @statemethod for public state methods that dispatch on the active instance
Log around generation calls, tool dispatch, and provider requests/responses without leaking secrets; prefer structured/concise messages
Add metrics via ctx.record where applicable
All I/O is async; keep boundaries async and use ctx.spawn for detached tasks
Use structured concurrency and valid coroutine usage; rely on haiway/asyncio; avoid custom threading
Construct multimodal content with MultimodalContent.of(...) and compose blocks explicitly
Use ResourceContent/ResourceReference for media/data blobs
Wrap custom types/data within ArtifactContent; use hidden when needed
Add NumPy-style docstrings for public symbols with Parameters/Returns/Raises and rationale when non-obvious
Avoid docstrings on internal helpers; keep names self-explanatory
Keep docstrings high-quality; mkdocstrings pulls them into API reference
Never log secrets or full request bodies containing keys/tokens

Files:

  • src/draive/multimodal/templates/variables.py
  • src/draive/generation/audio/default.py
  • src/draive/postgres/__init__.py
  • src/draive/openai/responses.py
  • src/draive/generation/text/default.py
  • src/draive/multimodal/templates/types.py
  • src/draive/generation/audio/types.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/helpers/instruction_refinement.py
  • src/draive/multimodal/templates/repository.py
  • src/draive/generation/text/types.py
  • src/draive/generation/image/state.py
  • src/draive/conversation/completion/types.py
  • src/draive/postgres/memory.py
  • src/draive/generation/image/default.py
  • src/draive/openai/images.py
  • src/draive/generation/model/state.py
  • src/draive/conversation/realtime/types.py
  • src/draive/__init__.py
  • src/draive/postgres/templates.py
  • src/draive/conversation/completion/state.py
  • src/draive/conversation/completion/default.py
  • src/draive/models/generative.py
  • src/draive/generation/model/default.py
  • src/draive/generation/image/types.py
  • src/draive/stages/stage.py
  • src/draive/generation/text/state.py
  • src/draive/generation/audio/state.py
  • src/draive/helpers/instruction_preparation.py
  • src/draive/generation/model/types.py
  • src/draive/conversation/realtime/default.py
  • src/draive/conversation/realtime/state.py
  • src/draive/multimodal/__init__.py
src/draive/multimodal/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement content types and helpers (MultimodalContent, TextContent, ArtifactContent) under draive/multimodal/

Files:

  • src/draive/multimodal/templates/variables.py
  • src/draive/multimodal/templates/types.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/multimodal/templates/repository.py
  • src/draive/multimodal/__init__.py
src/draive/{httpx,mcp,postgres,opentelemetry}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Place integrations under draive/httpx, draive/mcp, draive/postgres, draive/opentelemetry

Files:

  • src/draive/postgres/__init__.py
  • src/draive/postgres/memory.py
  • src/draive/postgres/templates.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.py: Do not perform real network I/O in unit tests; mock providers/HTTP
Keep tests fast and focused on changed code; start with unit tests around new types/functions/adapters
Use fixtures from tests/ or add focused ones; avoid heavy integration scaffolding
Use pytest-asyncio for coroutine tests (@pytest.mark.asyncio)
Prefer scoping with ctx.scope(...) in async tests and bind required State instances explicitly
Avoid real I/O and network in async tests; stub provider calls and HTTP

Files:

  • tests/test_multimodal_template_variables.py
  • tests/test_toolbox.py
  • tests/test_generative_merge.py
mkdocs.yml

📄 CodeRabbit inference engine (AGENTS.md)

Register documentation navigation updates in mkdocs.yml (nav section) when adding/adjusting pages

Files:

  • mkdocs.yml
src/draive/{openai,anthropic,mistral,gemini,vllm,ollama,bedrock,cohere}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/{openai,anthropic,mistral,gemini,vllm,ollama,bedrock,cohere}/**/*.py: Provider-specific feature modules live under their respective provider directories
Translate provider/SDK errors into typed exceptions; do not raise bare Exception and preserve context
Use environment variables for credentials and resolve via helper functions like getenv_str

Files:

  • src/draive/openai/responses.py
  • src/draive/openai/images.py
{pyproject.toml,pyrightconfig.json}

📄 CodeRabbit inference engine (AGENTS.md)

Use Ruff, Bandit, and Pyright (strict) via make lint

Files:

  • pyproject.toml
docs/**/*

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*: When behavior/API changes, update relevant docs under docs/ and examples as applicable
When adding public APIs, update examples/guides and ensure cross-links render

Files:

  • docs/guides/Templates.md
  • docs/guides/ComprehensiveEvaluation.md
  • docs/guides/Postgres.md
src/draive/conversation/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement higher-level chat/realtime conversations under draive/conversation/

Files:

  • src/draive/conversation/completion/types.py
  • src/draive/conversation/realtime/types.py
  • src/draive/conversation/completion/state.py
  • src/draive/conversation/completion/default.py
  • src/draive/conversation/realtime/default.py
  • src/draive/conversation/realtime/state.py
src/draive/__init__.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/__init__.py: Centralize public exports in src/draive/init.py
Update src/draive/init.py exports when API surface changes

Files:

  • src/draive/__init__.py
src/draive/models/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Keep core abstractions (GenerativeModel, tools, instructions) under draive/models/

Files:

  • src/draive/models/generative.py
src/draive/stages/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement pipeline stage abstractions and helpers under draive/stages/

Files:

  • src/draive/stages/stage.py
🧠 Learnings (6)
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/**/*.py : Add NumPy-style docstrings for public symbols with Parameters/Returns/Raises and rationale when non-obvious

Applied to files:

  • src/draive/multimodal/templates/types.py
  • src/draive/multimodal/templates/repository.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/__init__.py : Centralize public exports in src/draive/__init__.py

Applied to files:

  • src/draive/multimodal/templates/__init__.py
  • src/draive/__init__.py
  • src/draive/multimodal/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/__init__.py : Update src/draive/__init__.py exports when API surface changes

Applied to files:

  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/models/**/*.py : Keep core abstractions (GenerativeModel, tools, instructions) under draive/models/

Applied to files:

  • src/draive/__init__.py
  • src/draive/stages/stage.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/multimodal/**/*.py : Implement content types and helpers (MultimodalContent, TextContent, ArtifactContent) under draive/multimodal/

Applied to files:

  • src/draive/__init__.py
  • src/draive/multimodal/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/**/*.py : Import Haiway symbols directly (from haiway import State, ctx)

Applied to files:

  • src/draive/helpers/instruction_preparation.py
🧬 Code graph analysis (34)
src/draive/multimodal/templates/variables.py (1)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/generation/audio/default.py (5)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-426)
  • resolve_str (252-260)
  • resolve_str (263-271)
  • resolve_str (274-334)
src/draive/models/generative.py (6)
  • GenerativeModel (45-515)
  • completion (62-71)
  • completion (74-83)
  • completion (87-96)
  • completion (99-108)
  • completion (111-181)
src/draive/conversation/completion/state.py (3)
  • completion (34-43)
  • completion (47-56)
  • completion (59-136)
src/draive/stages/stage.py (1)
  • completion (288-372)
src/draive/postgres/__init__.py (2)
src/draive/multimodal/templates/repository.py (3)
  • templates (137-140)
  • templates (143-146)
  • templates (149-165)
src/draive/postgres/templates.py (1)
  • PostgresTemplatesRepository (28-173)
tests/test_multimodal_template_variables.py (3)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/variables.py (3)
  • parse_template_variables (14-21)
  • resolve_multimodal_template (48-67)
  • resolve_text_template (24-45)
src/draive/multimodal/text.py (1)
  • TextContent (11-82)
src/draive/openai/responses.py (1)
src/draive/models/types.py (1)
  • ModelReasoning (518-562)
src/draive/generation/text/default.py (3)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-426)
  • resolve_str (252-260)
  • resolve_str (263-271)
  • resolve_str (274-334)
src/draive/models/generative.py (5)
  • loop (185-194)
  • loop (197-206)
  • loop (210-219)
  • loop (222-231)
  • loop (234-306)
src/draive/multimodal/templates/types.py (2)
tests/test_toolbox.py (4)
  • meta (109-110)
  • meta (310-311)
  • description (97-98)
  • description (298-299)
src/draive/stages/stage.py (1)
  • with_meta (1390-1435)
src/draive/generation/audio/types.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/multimodal/templates/__init__.py (2)
src/draive/multimodal/templates/repository.py (4)
  • templates (137-140)
  • templates (143-146)
  • templates (149-165)
  • TemplatesRepository (61-426)
src/draive/multimodal/templates/types.py (6)
  • Template (39-139)
  • TemplateDeclaration (143-199)
  • TemplateDefining (225-236)
  • TemplateListing (203-209)
  • TemplateLoading (213-221)
  • TemplateMissing (18-35)
src/draive/helpers/instruction_refinement.py (5)
src/draive/multimodal/content.py (2)
  • MultimodalContent (25-592)
  • MultimodalTag (599-700)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-426)
  • loading (439-445)
  • loading (514-524)
  • load (337-371)
src/draive/multimodal/text.py (1)
  • TextContent (11-82)
src/draive/postgres/templates.py (2)
  • loading (121-126)
  • load (88-119)
src/draive/multimodal/templates/repository.py (3)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (8)
  • Template (39-139)
  • TemplateDeclaration (143-199)
  • TemplateDefining (225-236)
  • TemplateListing (203-209)
  • TemplateLoading (213-221)
  • TemplateMissing (18-35)
  • of (56-84)
  • of (162-194)
src/draive/multimodal/templates/variables.py (3)
  • parse_template_variables (14-21)
  • resolve_multimodal_template (48-67)
  • resolve_text_template (24-45)
src/draive/generation/text/types.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/generation/image/state.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/conversation/completion/types.py (2)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/postgres/memory.py (1)
src/draive/models/types.py (3)
  • ModelInput (429-501)
  • ModelMemoryRecall (719-766)
  • ModelOutput (573-672)
src/draive/generation/image/default.py (3)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (252-260)
  • resolve_str (263-271)
  • resolve_str (274-334)
src/draive/models/generative.py (5)
  • completion (62-71)
  • completion (74-83)
  • completion (87-96)
  • completion (99-108)
  • completion (111-181)
src/draive/openai/images.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-426)
  • resolve_str (252-260)
  • resolve_str (263-271)
  • resolve_str (274-334)
src/draive/generation/model/state.py (3)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/conversation/realtime/types.py (2)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/__init__.py (2)
src/draive/multimodal/templates/types.py (3)
  • Template (39-139)
  • TemplateDeclaration (143-199)
  • TemplateMissing (18-35)
src/draive/multimodal/templates/repository.py (1)
  • TemplatesRepository (61-426)
src/draive/postgres/templates.py (2)
src/draive/multimodal/templates/repository.py (11)
  • templates (137-140)
  • templates (143-146)
  • templates (149-165)
  • TemplatesRepository (61-426)
  • listing (433-437)
  • listing (504-512)
  • load (337-371)
  • loading (439-445)
  • loading (514-524)
  • defining (447-462)
  • defining (526-545)
src/draive/multimodal/templates/types.py (1)
  • TemplateDeclaration (143-199)
src/draive/conversation/completion/state.py (3)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/conversation/completion/default.py (5)
src/draive/models/types.py (4)
  • ModelMemoryRecall (719-766)
  • ModelOutput (573-672)
  • ModelReasoning (518-562)
  • ModelToolRequest (311-353)
src/draive/multimodal/artifact.py (1)
  • ArtifactContent (11-96)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (252-260)
  • resolve_str (263-271)
  • resolve_str (274-334)
src/draive/models/generative.py (6)
  • GenerativeModel (45-515)
  • loop (185-194)
  • loop (197-206)
  • loop (210-219)
  • loop (222-231)
  • loop (234-306)
src/draive/generation/model/default.py (4)
src/draive/multimodal/templates/types.py (2)
  • Template (39-139)
  • with_arguments (90-113)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (252-260)
  • resolve_str (263-271)
  • resolve_str (274-334)
src/draive/parameters/model.py (2)
  • json_schema (443-451)
  • simplified_schema (433-440)
src/draive/parameters/schema.py (1)
  • simplified_schema (9-26)
src/draive/generation/image/types.py (1)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/stages/stage.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-426)
  • resolve_str (252-260)
  • resolve_str (263-271)
  • resolve_str (274-334)
src/draive/generation/text/state.py (3)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/generation/audio/state.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
tests/test_generative_merge.py (1)
src/draive/models/types.py (2)
  • ModelReasoning (518-562)
  • ModelToolRequest (311-353)
src/draive/helpers/instruction_preparation.py (1)
src/draive/multimodal/templates/types.py (1)
  • TemplateDeclaration (143-199)
src/draive/generation/model/types.py (3)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/conversation/realtime/default.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/multimodal/templates/repository.py (3)
  • resolve_str (252-260)
  • resolve_str (263-271)
  • resolve_str (274-334)
src/draive/conversation/realtime/state.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/multimodal/__init__.py (2)
src/draive/multimodal/templates/repository.py (4)
  • templates (137-140)
  • templates (143-146)
  • templates (149-165)
  • TemplatesRepository (61-426)
src/draive/multimodal/templates/types.py (6)
  • Template (39-139)
  • TemplateDeclaration (143-199)
  • TemplateDefining (225-236)
  • TemplateListing (203-209)
  • TemplateLoading (213-221)
  • TemplateMissing (18-35)
🪛 LanguageTool
docs/guides/Templates.md

[style] ~104-~104: It’s more common nowadays to write this noun as one word.
Context: ...fining templates programmatically, pass variables={"user": "User name"} to document expected arguments. This...

(RECOMMENDED_COMPOUNDS)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (9)
src/draive/stages/stage.py (1)

2054-2078: Template resolution on plain string is safe but potentially unnecessary.

Line 2054 creates a plain string without template variables, and line 2075 resolves it via TemplatesRepository.resolve_str(). Since the input is a plain string, the resolution will return it as-is (based on the repository implementation in relevant snippets).

This works correctly but adds unnecessary overhead. Consider passing the string directly to GenerativeModel.loop() or verify if future template variable substitution is intended.

If no template variables are needed, apply this diff:

-    instructions: str = (
+    routing_instructions: str = (
         "Based on the provided context and the current result,"  # nosec: B608 - false positive
         " select the most appropriate option from the following:"
         f"\n\n{options_text}"
         "\n\nRespond with with the exact option name within SELECTION xml tag"
         f" like (e.g., '<SELECTION>{next(iter(options.keys()))}</SELECTION>'"
     )

     # Create routing context with the current result as input
     routing_context: list[ModelContextElement] = [
         *state.context,
         ModelInput.of(
             MultimodalContent.of(
                 "<RESULT>",
                 state.result,
                 "</RESULT>",
             )
         ),
     ]

     result: ModelOutput = await GenerativeModel.loop(
-        instructions=await TemplatesRepository.resolve_str(instructions),
+        instructions=routing_instructions,
         context=routing_context,
         output="text",
     )
src/draive/generation/model/state.py (1)

15-16: Add NumPy‑style docstrings for the public API.
Please document ModelGeneration.generate parameters/returns/raises for mkdocstrings.

As per coding guidelines

Also applies to: 47-60

src/draive/generation/model/types.py (1)

14-20: Document public protocols.
Please add brief docstrings to ModelGenerationDecoder and ModelGenerating describing parameters and return types.

As per coding guidelines

Also applies to: 22-36

src/draive/generation/audio/default.py (1)

24-29: Forward **extra to template resolution.
Template backends may rely on this context; not forwarding can break resolution.

Apply:

-            instructions=await TemplatesRepository.resolve_str(instructions),
+            instructions=await TemplatesRepository.resolve_str(instructions, **extra),
src/draive/conversation/realtime/default.py (1)

70-75: Use ctx.log_warn, not ctx.log_warning (runtime error risk).

haiway exposes ctx.log_debug/info/warn/error. log_warning likely does not exist.

Apply:

-                    ctx.log_warning(
+                    ctx.log_warn(
                         f"Tool handling `{response.handling}` is not supported in"
                         " realtime conversation, using regular result handling instead"
                     )

As per coding guidelines.

src/draive/models/generative.py (2)

650-666: Update docstring: no longer resolved via InstructionsRepository.

Implementation passes instructions through; resolution happens in callers (e.g., realtime default). Remove outdated paragraph.

Apply:

-        Resolves ``instructions`` (via ``InstructionsRepository``), normalizes the provided
-        ``memory`` into a ``ModelMemory`` if a raw context is given, and delegates to the
-        provider's ``session_preparing`` to obtain a ``ModelSessionScope``.
+        Normalizes the provided ``memory`` into a ``ModelMemory`` if a raw context is
+        given, and delegates to the provider's ``session_preparing`` to obtain a
+        ``ModelSessionScope``.

398-404: Nit: fix typo in comment.

Apply:

-            # contunue to the next round with results
+            # continue to the next round with results
src/draive/conversation/completion/state.py (1)

37-43: Include str in instructions union and docs.

Default is ""; types exclude str. Fix all 3 signatures and the docstring param type.

Apply:

-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",

And in the docstring:

-        instructions : Template | ModelInstructions, optional
+        instructions : Template | ModelInstructions | str, optional

Also applies to: 50-56, 62-69, 73-75

src/draive/conversation/completion/default.py (1)

175-176: Avoid assert for runtime filtering of ModelToolRequest.

Asserts can be stripped with -O; filter explicitly or log.

Apply this diff:

-            else:
-                assert not isinstance(chunk, ModelToolRequest)  # nosec: B101
-                yield ConversationOutputChunk.of(chunk)
+            else:
+                if isinstance(chunk, ModelToolRequest):
+                    ctx.log_warn("Dropping unexpected ModelToolRequest chunk in conversation stream")
+                    continue
+                yield ConversationOutputChunk.of(chunk)
♻️ Duplicate comments (23)
src/draive/multimodal/templates/variables.py (5)

7-11: Fix __all__: duplicate export and missing function.

The __all__ tuple exports "resolve_multimodal_template" twice and omits "resolve_text_template". Although a past review indicated this was addressed, the current code still has the issue.

Apply this diff:

 __all__ = (
     "parse_template_variables",
-    "resolve_multimodal_template",
-    "resolve_multimodal_template",
+    "resolve_text_template",
+    "resolve_multimodal_template",
 )

1-1: Use built-in generics (set[str]) instead of Set from collections.abc.

Per coding guidelines for Python 3.12+, use built-in generic types.

Apply this diff:

-from collections.abc import Generator, Mapping, MutableSequence, Set
+from collections.abc import Generator, Mapping, MutableSequence

Then update all occurrences of Set[str] to set[str] at lines 16, and in the implementation where needed.

As per coding guidelines.


14-21: Add NumPy-style docstring for public function.

Public functions require NumPy-style documentation per coding guidelines.

Example docstring:

def parse_template_variables(
    template: str,
) -> set[str]:
    """Extract variable names from a template string.
    
    Parameters
    ----------
    template : str
        Template string containing variable placeholders in {%name%} format.
    
    Returns
    -------
    set[str]
        Set of unique variable names found in the template.
    """

As per coding guidelines.


24-45: Add NumPy-style docstring for public function.

Public functions require NumPy-style documentation per coding guidelines.

Example docstring:

def resolve_text_template(
    template: str,
    *,
    arguments: Mapping[str, Multimodal],
) -> str:
    """Resolve template variables into a text string.
    
    Parameters
    ----------
    template : str
        Template string with {%variable%} placeholders.
    arguments : Mapping[str, Multimodal]
        Mapping of variable names to multimodal values.
    
    Returns
    -------
    str
        Resolved template string with all variables substituted.
    
    Raises
    ------
    KeyError
        When a required template variable is missing from arguments.
    """

As per coding guidelines.


48-67: Add NumPy-style docstring for public function.

Public functions require NumPy-style documentation per coding guidelines.

Example docstring:

def resolve_multimodal_template(
    template: str,
    *,
    arguments: Mapping[str, Multimodal],
) -> MultimodalContent:
    """Resolve template variables into multimodal content.
    
    Parameters
    ----------
    template : str
        Template string with {%variable%} placeholders.
    arguments : Mapping[str, Multimodal]
        Mapping of variable names to multimodal content or strings.
    
    Returns
    -------
    MultimodalContent
        Normalized multimodal content with variables resolved.
    
    Raises
    ------
    KeyError
        When a required template variable is missing from arguments.
    """

As per coding guidelines.

src/draive/generation/text/types.py (1)

10-20: Add a concise docstring to TextGenerating.
Document parameters/return type for API reference.

As per coding guidelines

src/draive/generation/image/default.py (1)

24-29: Forward **extra to TemplatesRepository.resolve_str.
Required for backends needing context; currently dropped.

Apply:

-            instructions=await TemplatesRepository.resolve_str(instructions),
+            instructions=await TemplatesRepository.resolve_str(instructions, **extra),
src/draive/openai/images.py (1)

33-41: Pass **extra into TemplatesRepository.resolve_str.
Currently omitted; can cause template resolution failures.

Apply:

-                prompt=await TemplatesRepository.resolve_str(instructions),
+                prompt=await TemplatesRepository.resolve_str(instructions, **extra),
src/draive/generation/text/default.py (1)

28-28: Forward **extra to template resolution for backend context.

TemplatesRepository.resolve_str accepts **extra to allow backends (e.g., PostgreSQL, custom adapters) to receive contextual parameters such as revision hints, cache controls, or tenant identifiers. Currently, **extra is forwarded to GenerativeModel.loop but not to template resolution, which may prevent advanced template backends from functioning correctly.

Apply this diff:

-            instructions=await TemplatesRepository.resolve_str(instructions),
+            instructions=await TemplatesRepository.resolve_str(instructions, **extra),
src/draive/generation/text/state.py (1)

20-20: Type-default mismatch: add | str to union.

The instructions parameter has a default value of "" (a str), but the type annotation Template | ModelInstructions does not include str. This causes a type inconsistency across all three signatures.

Apply this diff to fix all three signatures:

-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",

Also applies to: 31-31, 42-42

src/draive/generation/audio/state.py (1)

20-20: Type-default mismatch: include | str where default is "".

The instructions parameter has a default of "" (a str), but str is not in the union. Add | str to all three signatures to keep the API consistent and satisfy typing.

Apply this diff:

-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",

Also applies to: 29-29, 38-38

src/draive/helpers/instruction_preparation.py (2)

25-30: Add NumPy-style docstring to public helper.

Public helper prepare_instructions lacks documentation. Per coding guidelines, add a NumPy-style docstring documenting inputs, outputs, and the ambiguity behavior.

Apply this diff:

 async def prepare_instructions(
     instruction: TemplateDeclaration | str,
     /,
     *,
     guidelines: str | None = None,
 ) -> ModelInstructions:
+    """Prepare precise model instructions from a description or declaration.
+
+    Parameters
+    ----------
+    instruction : TemplateDeclaration | str
+        Either a template declaration (variables + description) or raw task description.
+    guidelines : str | None, optional
+        Extra guidance appended to the preparation prompt.
+
+    Returns
+    -------
+    ModelInstructions
+        Prepared instruction string.
+
+    Raises
+    ------
+    InstructionPreparationAmbiguity
+        When clarification questions are required.
+    ValueError
+        On unexpected preparation result.
+    """

37-40: Prefer TemplateDeclaration.of(...) for State construction.

Use the .of() constructor helper rather than direct dataclass construction for consistency with State construction patterns across the codebase.

Apply this diff:

-                instruction_declaration = TemplateDeclaration(
-                    identifier="instruction",
+                instruction_declaration = TemplateDeclaration.of(
+                    identifier="instruction",
                     description=description,
                     variables={},
                 )
src/draive/postgres/templates.py (1)

74-82: Prefer TemplateDeclaration.of(...) for State construction.

Use the .of() constructor helper rather than direct dataclass construction for consistency with State construction patterns and future-proofing.

Apply this diff:

         return tuple(
-            TemplateDeclaration(
+            TemplateDeclaration.of(
                 identifier=cast(str, result["identifier"]),
                 description=cast(str | None, result["description"]),
                 variables=json.loads(cast(str, result["variables"] or "{}")),
                 meta=Meta.from_json(cast(str, result["meta"] or "{}")),
             )
             for result in results
         )
src/draive/conversation/realtime/default.py (1)

52-56: Forward context: pass **extra to TemplatesRepository.resolve_str.

Backends may require extra context (tenant, locale). Not forwarding it can mis-resolve templates.

Apply:

-        instructions=await TemplatesRepository.resolve_str(instructions),
+        instructions=await TemplatesRepository.resolve_str(instructions, **extra),
src/draive/generation/image/state.py (1)

20-23: Type-default mismatch: include str in instructions union.

Default is ""; annotation excludes str. Fix all three signatures.

Apply:

-        instructions: Template | ModelInstructions = "",
+        instructions: Template | ModelInstructions | str = "",

(repeat for Lines 29 and 38)

Also applies to: 29-33, 38-41

src/draive/multimodal/templates/repository.py (5)

436-441: Return a stable order.

Sort declarations by identifier to avoid nondeterminism/flaky tests.

Apply:

-        return tuple(self._declarations.values())
+        return tuple(sorted(self._declarations.values(), key=lambda d: d.identifier))

511-516: Return a stable order.

Same as VolatileStorage.listing — sort by identifier.

Apply:

-        return tuple(self._declarations.values())
+        return tuple(sorted(self._declarations.values(), key=lambda d: d.identifier))

581-583: Use ctx.log_warn (not ctx.log_warning).

Wrong method name risks runtime failure.

Apply:

-                                ctx.log_warning(
+                                ctx.log_warn(
                                     "Invalid templates file storage element, skipping..."
                                 )

As per coding guidelines.


467-475: Consistent naming: use identifier instead of name.

Keep API coherent with listing/loading/defining.

Apply:

-    async def removing(
-        self,
-        name: str,
+    async def removing(
+        self,
+        identifier: str,
         meta: Meta,
         **extra: Any,
     ) -> None:
-        del self._declarations[name]
-        del self._contents[name]
+        del self._declarations[identifier]
+        del self._contents[identifier]

610-621: Write templates in a stable, reproducible order.

Sort declarations and consider sort_keys=True to stabilize nested dicts.

Apply:

-        file_contents: bytes = json.dumps(
-            [
-                {
-                    "identifier": declaration.identifier,
-                    "description": declaration.description,
-                    "variables": declaration.variables,
-                    "content": self._contents[declaration.identifier],
-                    "meta": declaration.meta.to_mapping(),
-                }
-                for declaration in self._declarations.values()
-            ]
-        ).encode()
+        ordered = sorted(self._declarations.values(), key=lambda d: d.identifier)
+        file_contents: bytes = json.dumps(
+            [
+                {
+                    "identifier": d.identifier,
+                    "description": d.description,
+                    "variables": d.variables,
+                    "content": self._contents[d.identifier],
+                    "meta": d.meta.to_mapping(),
+                }
+                for d in ordered
+            ],
+            sort_keys=True,
+        ).encode()
src/draive/conversation/completion/default.py (1)

97-105: Normalize template-variable conversion and build the mapping once.

Prefer value.to_str() when available; reuse the mapping. This was suggested earlier.

Apply this diff for the non‑streaming path:

-        resolved_instructions: str = await TemplatesRepository.resolve_str(
-            instructions,
-            arguments={
-                key: value if isinstance(value, str) else str(value)
-                for key, value in memory_recall.variables.items()
-            }
-            if memory_recall.variables
-            else None,
-        )
+        args = (
+            {
+                key: (
+                    value
+                    if isinstance(value, str)
+                    else (value.to_str() if hasattr(value, "to_str") else str(value))
+                )
+                for key, value in memory_recall.variables.items()
+            }
+            if memory_recall.variables
+            else None
+        )
+        resolved_instructions: str = await TemplatesRepository.resolve_str(
+            instructions,
+            arguments=args,
+        )

And in the streaming path (after adopting the previous fix), ensure you pass the same args var (already included in that diff).

Also applies to: 150-158

src/draive/multimodal/templates/types.py (1)

228-236: Align TemplateDefining parameter order with repository usage.

Keep (identifier, description, variables, content, meta) for clarity and consistency. Previously raised.

Apply this diff:

 @runtime_checkable
 class TemplateDefining(Protocol):
     """Callable protocol that upserts template content and metadata."""
 
     async def __call__(
         self,
         identifier: str,
         description: str | None,
-        content: str,
         variables: Mapping[str, str],
+        content: str,
         meta: Meta,
         **extra: Any,
     ) -> None: ...
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 92beca4 and a9ef300.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (48)
  • Makefile (1 hunks)
  • docs/guides/ComprehensiveEvaluation.md (3 hunks)
  • docs/guides/Postgres.md (3 hunks)
  • docs/guides/Templates.md (1 hunks)
  • mkdocs.yml (1 hunks)
  • pyproject.toml (1 hunks)
  • src/draive/__init__.py (2 hunks)
  • src/draive/conversation/completion/default.py (7 hunks)
  • src/draive/conversation/completion/state.py (5 hunks)
  • src/draive/conversation/completion/types.py (4 hunks)
  • src/draive/conversation/realtime/default.py (3 hunks)
  • src/draive/conversation/realtime/state.py (3 hunks)
  • src/draive/conversation/realtime/types.py (2 hunks)
  • src/draive/generation/audio/default.py (1 hunks)
  • src/draive/generation/audio/state.py (4 hunks)
  • src/draive/generation/audio/types.py (2 hunks)
  • src/draive/generation/image/default.py (1 hunks)
  • src/draive/generation/image/state.py (4 hunks)
  • src/draive/generation/image/types.py (2 hunks)
  • src/draive/generation/model/default.py (3 hunks)
  • src/draive/generation/model/state.py (4 hunks)
  • src/draive/generation/model/types.py (2 hunks)
  • src/draive/generation/text/default.py (1 hunks)
  • src/draive/generation/text/state.py (4 hunks)
  • src/draive/generation/text/types.py (2 hunks)
  • src/draive/helpers/instruction_preparation.py (3 hunks)
  • src/draive/helpers/instruction_refinement.py (5 hunks)
  • src/draive/models/__init__.py (0 hunks)
  • src/draive/models/generative.py (5 hunks)
  • src/draive/models/instructions/__init__.py (0 hunks)
  • src/draive/models/instructions/repository.py (0 hunks)
  • src/draive/models/instructions/template.py (0 hunks)
  • src/draive/models/instructions/types.py (0 hunks)
  • src/draive/multimodal/__init__.py (2 hunks)
  • src/draive/multimodal/templates/__init__.py (1 hunks)
  • src/draive/multimodal/templates/repository.py (1 hunks)
  • src/draive/multimodal/templates/types.py (1 hunks)
  • src/draive/multimodal/templates/variables.py (1 hunks)
  • src/draive/openai/images.py (3 hunks)
  • src/draive/openai/responses.py (1 hunks)
  • src/draive/postgres/__init__.py (2 hunks)
  • src/draive/postgres/instructions.py (0 hunks)
  • src/draive/postgres/memory.py (1 hunks)
  • src/draive/postgres/templates.py (1 hunks)
  • src/draive/stages/stage.py (15 hunks)
  • tests/test_generative_merge.py (1 hunks)
  • tests/test_multimodal_template_variables.py (1 hunks)
  • tests/test_toolbox.py (1 hunks)
💤 Files with no reviewable changes (6)
  • src/draive/models/instructions/repository.py
  • src/draive/models/instructions/init.py
  • src/draive/models/init.py
  • src/draive/models/instructions/types.py
  • src/draive/postgres/instructions.py
  • src/draive/models/instructions/template.py
🧰 Additional context used
📓 Path-based instructions (13)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use Python 3.12+ features and syntax across the codebase
Format code exclusively with Ruff (make format); do not use other formatters
Skip module-level docstrings

Files:

  • src/draive/multimodal/templates/variables.py
  • src/draive/multimodal/__init__.py
  • src/draive/helpers/instruction_refinement.py
  • src/draive/helpers/instruction_preparation.py
  • src/draive/postgres/__init__.py
  • tests/test_toolbox.py
  • src/draive/generation/model/types.py
  • src/draive/generation/audio/state.py
  • src/draive/generation/audio/types.py
  • src/draive/generation/audio/default.py
  • src/draive/stages/stage.py
  • src/draive/postgres/templates.py
  • src/draive/generation/image/types.py
  • src/draive/conversation/realtime/state.py
  • src/draive/conversation/realtime/default.py
  • src/draive/models/generative.py
  • src/draive/generation/text/default.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/conversation/completion/types.py
  • src/draive/postgres/memory.py
  • src/draive/openai/images.py
  • src/draive/generation/model/state.py
  • src/draive/__init__.py
  • tests/test_generative_merge.py
  • src/draive/conversation/realtime/types.py
  • src/draive/openai/responses.py
  • src/draive/generation/image/default.py
  • src/draive/generation/text/state.py
  • src/draive/multimodal/templates/repository.py
  • src/draive/conversation/completion/state.py
  • src/draive/generation/text/types.py
  • src/draive/conversation/completion/default.py
  • tests/test_multimodal_template_variables.py
  • src/draive/generation/image/state.py
  • src/draive/multimodal/templates/types.py
  • src/draive/generation/model/default.py
src/draive/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/**/*.py: Import Haiway symbols directly (from haiway import State, ctx)
Use ctx.scope(...) to bind scoped Disposables and active State; avoid global state
Route all logs through ctx.log_debug/info/warn/error; do not use print
Use latest, most strict typing syntax (Python 3.12+), with strict typing only for public APIs
Avoid loose Any except at explicit third‑party boundaries
Prefer explicit attribute access with static types; avoid dynamic getattr except at narrow boundaries
Prefer Mapping/Sequence/Iterable in public types over dict/list/set
Use final where applicable; avoid inheritance and prefer composition
Use precise unions (|) and narrow with match/isinstance; avoid cast unless provably safe and localized
Model immutable data/config and facades with haiway.State; provide ergonomic classmethods like .of(...)
Avoid in-place mutation; use State.updated(...) or functional builders to produce new instances
Access active state via haiway.ctx inside async scopes (ctx.scope(...))
Use @statemethod for public state methods that dispatch on the active instance
Log around generation calls, tool dispatch, and provider requests/responses without leaking secrets; prefer structured/concise messages
Add metrics via ctx.record where applicable
All I/O is async; keep boundaries async and use ctx.spawn for detached tasks
Use structured concurrency and valid coroutine usage; rely on haiway/asyncio; avoid custom threading
Construct multimodal content with MultimodalContent.of(...) and compose blocks explicitly
Use ResourceContent/ResourceReference for media/data blobs
Wrap custom types/data within ArtifactContent; use hidden when needed
Add NumPy-style docstrings for public symbols with Parameters/Returns/Raises and rationale when non-obvious
Avoid docstrings on internal helpers; keep names self-explanatory
Keep docstrings high-quality; mkdocstrings pulls them into API reference
Never log secrets or full request bodies containing keys/tokens

Files:

  • src/draive/multimodal/templates/variables.py
  • src/draive/multimodal/__init__.py
  • src/draive/helpers/instruction_refinement.py
  • src/draive/helpers/instruction_preparation.py
  • src/draive/postgres/__init__.py
  • src/draive/generation/model/types.py
  • src/draive/generation/audio/state.py
  • src/draive/generation/audio/types.py
  • src/draive/generation/audio/default.py
  • src/draive/stages/stage.py
  • src/draive/postgres/templates.py
  • src/draive/generation/image/types.py
  • src/draive/conversation/realtime/state.py
  • src/draive/conversation/realtime/default.py
  • src/draive/models/generative.py
  • src/draive/generation/text/default.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/conversation/completion/types.py
  • src/draive/postgres/memory.py
  • src/draive/openai/images.py
  • src/draive/generation/model/state.py
  • src/draive/__init__.py
  • src/draive/conversation/realtime/types.py
  • src/draive/openai/responses.py
  • src/draive/generation/image/default.py
  • src/draive/generation/text/state.py
  • src/draive/multimodal/templates/repository.py
  • src/draive/conversation/completion/state.py
  • src/draive/generation/text/types.py
  • src/draive/conversation/completion/default.py
  • src/draive/generation/image/state.py
  • src/draive/multimodal/templates/types.py
  • src/draive/generation/model/default.py
src/draive/multimodal/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement content types and helpers (MultimodalContent, TextContent, ArtifactContent) under draive/multimodal/

Files:

  • src/draive/multimodal/templates/variables.py
  • src/draive/multimodal/__init__.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/multimodal/templates/repository.py
  • src/draive/multimodal/templates/types.py
docs/**/*

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*: When behavior/API changes, update relevant docs under docs/ and examples as applicable
When adding public APIs, update examples/guides and ensure cross-links render

Files:

  • docs/guides/ComprehensiveEvaluation.md
  • docs/guides/Postgres.md
  • docs/guides/Templates.md
src/draive/{httpx,mcp,postgres,opentelemetry}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Place integrations under draive/httpx, draive/mcp, draive/postgres, draive/opentelemetry

Files:

  • src/draive/postgres/__init__.py
  • src/draive/postgres/templates.py
  • src/draive/postgres/memory.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.py: Do not perform real network I/O in unit tests; mock providers/HTTP
Keep tests fast and focused on changed code; start with unit tests around new types/functions/adapters
Use fixtures from tests/ or add focused ones; avoid heavy integration scaffolding
Use pytest-asyncio for coroutine tests (@pytest.mark.asyncio)
Prefer scoping with ctx.scope(...) in async tests and bind required State instances explicitly
Avoid real I/O and network in async tests; stub provider calls and HTTP

Files:

  • tests/test_toolbox.py
  • tests/test_generative_merge.py
  • tests/test_multimodal_template_variables.py
src/draive/stages/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement pipeline stage abstractions and helpers under draive/stages/

Files:

  • src/draive/stages/stage.py
mkdocs.yml

📄 CodeRabbit inference engine (AGENTS.md)

Register documentation navigation updates in mkdocs.yml (nav section) when adding/adjusting pages

Files:

  • mkdocs.yml
src/draive/conversation/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Implement higher-level chat/realtime conversations under draive/conversation/

Files:

  • src/draive/conversation/realtime/state.py
  • src/draive/conversation/realtime/default.py
  • src/draive/conversation/completion/types.py
  • src/draive/conversation/realtime/types.py
  • src/draive/conversation/completion/state.py
  • src/draive/conversation/completion/default.py
src/draive/models/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Keep core abstractions (GenerativeModel, tools, instructions) under draive/models/

Files:

  • src/draive/models/generative.py
src/draive/{openai,anthropic,mistral,gemini,vllm,ollama,bedrock,cohere}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/{openai,anthropic,mistral,gemini,vllm,ollama,bedrock,cohere}/**/*.py: Provider-specific feature modules live under their respective provider directories
Translate provider/SDK errors into typed exceptions; do not raise bare Exception and preserve context
Use environment variables for credentials and resolve via helper functions like getenv_str

Files:

  • src/draive/openai/images.py
  • src/draive/openai/responses.py
src/draive/__init__.py

📄 CodeRabbit inference engine (AGENTS.md)

src/draive/__init__.py: Centralize public exports in src/draive/init.py
Update src/draive/init.py exports when API surface changes

Files:

  • src/draive/__init__.py
{pyproject.toml,pyrightconfig.json}

📄 CodeRabbit inference engine (AGENTS.md)

Use Ruff, Bandit, and Pyright (strict) via make lint

Files:

  • pyproject.toml
🧠 Learnings (6)
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/__init__.py : Centralize public exports in src/draive/__init__.py

Applied to files:

  • src/draive/multimodal/__init__.py
  • src/draive/multimodal/templates/__init__.py
  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/multimodal/**/*.py : Implement content types and helpers (MultimodalContent, TextContent, ArtifactContent) under draive/multimodal/

Applied to files:

  • src/draive/multimodal/__init__.py
  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/**/*.py : Import Haiway symbols directly (from haiway import State, ctx)

Applied to files:

  • src/draive/helpers/instruction_preparation.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/models/**/*.py : Keep core abstractions (GenerativeModel, tools, instructions) under draive/models/

Applied to files:

  • src/draive/stages/stage.py
  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/__init__.py : Update src/draive/__init__.py exports when API surface changes

Applied to files:

  • src/draive/__init__.py
📚 Learning: 2025-10-03T08:51:45.502Z
Learnt from: CR
PR: miquido/draive#0
File: AGENTS.md:0-0
Timestamp: 2025-10-03T08:51:45.502Z
Learning: Applies to src/draive/**/*.py : Add NumPy-style docstrings for public symbols with Parameters/Returns/Raises and rationale when non-obvious

Applied to files:

  • src/draive/multimodal/templates/repository.py
  • src/draive/multimodal/templates/types.py
🧬 Code graph analysis (34)
src/draive/multimodal/templates/variables.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (2)
  • of (56-84)
  • of (162-194)
src/draive/multimodal/__init__.py (2)
src/draive/multimodal/templates/repository.py (4)
  • templates (137-140)
  • templates (143-146)
  • templates (149-165)
  • TemplatesRepository (61-429)
src/draive/multimodal/templates/types.py (6)
  • Template (39-139)
  • TemplateDeclaration (143-199)
  • TemplateDefining (225-236)
  • TemplateListing (203-209)
  • TemplateLoading (213-221)
  • TemplateMissing (18-35)
src/draive/helpers/instruction_refinement.py (4)
src/draive/multimodal/content.py (2)
  • MultimodalContent (25-592)
  • MultimodalTag (599-700)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-429)
  • loading (442-448)
  • loading (517-527)
  • load (340-374)
src/draive/multimodal/text.py (1)
  • TextContent (11-82)
src/draive/helpers/instruction_preparation.py (1)
src/draive/multimodal/templates/types.py (1)
  • TemplateDeclaration (143-199)
src/draive/postgres/__init__.py (2)
src/draive/multimodal/templates/repository.py (3)
  • templates (137-140)
  • templates (143-146)
  • templates (149-165)
src/draive/postgres/templates.py (1)
  • PostgresTemplatesRepository (28-173)
src/draive/generation/model/types.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/generation/audio/state.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/generation/audio/types.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/generation/audio/default.py (4)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-429)
  • resolve_str (252-260)
  • resolve_str (263-271)
  • resolve_str (274-337)
src/draive/models/generative.py (6)
  • GenerativeModel (45-515)
  • completion (62-71)
  • completion (74-83)
  • completion (87-96)
  • completion (99-108)
  • completion (111-181)
src/draive/stages/stage.py (1)
  • completion (288-372)
src/draive/stages/stage.py (2)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-429)
  • resolve_str (252-260)
  • resolve_str (263-271)
  • resolve_str (274-337)
src/draive/postgres/templates.py (3)
src/draive/multimodal/templates/repository.py (11)
  • templates (137-140)
  • templates (143-146)
  • templates (149-165)
  • TemplatesRepository (61-429)
  • listing (436-440)
  • listing (507-515)
  • load (340-374)
  • loading (442-448)
  • loading (517-527)
  • defining (450-465)
  • defining (529-548)
src/draive/multimodal/templates/types.py (1)
  • TemplateDeclaration (143-199)
src/draive/parameters/model.py (2)
  • from_json (454-469)
  • to_json (501-517)
src/draive/generation/image/types.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/conversation/realtime/state.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/conversation/realtime/default.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/generation/text/default.py (3)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-429)
  • resolve_str (252-260)
  • resolve_str (263-271)
  • resolve_str (274-337)
src/draive/models/generative.py (6)
  • GenerativeModel (45-515)
  • loop (185-194)
  • loop (197-206)
  • loop (210-219)
  • loop (222-231)
  • loop (234-306)
src/draive/multimodal/templates/__init__.py (2)
src/draive/multimodal/templates/repository.py (4)
  • templates (137-140)
  • templates (143-146)
  • templates (149-165)
  • TemplatesRepository (61-429)
src/draive/multimodal/templates/types.py (6)
  • Template (39-139)
  • TemplateDeclaration (143-199)
  • TemplateDefining (225-236)
  • TemplateListing (203-209)
  • TemplateLoading (213-221)
  • TemplateMissing (18-35)
src/draive/conversation/completion/types.py (2)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/postgres/memory.py (1)
src/draive/models/types.py (3)
  • ModelInput (429-501)
  • ModelMemoryRecall (719-766)
  • ModelOutput (573-672)
src/draive/openai/images.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/generation/model/state.py (3)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/__init__.py (2)
src/draive/multimodal/templates/types.py (3)
  • Template (39-139)
  • TemplateDeclaration (143-199)
  • TemplateMissing (18-35)
src/draive/multimodal/templates/repository.py (1)
  • TemplatesRepository (61-429)
tests/test_generative_merge.py (1)
src/draive/models/types.py (2)
  • ModelReasoning (518-562)
  • ModelToolRequest (311-353)
src/draive/conversation/realtime/types.py (2)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/openai/responses.py (1)
src/draive/models/types.py (1)
  • ModelReasoning (518-562)
src/draive/generation/image/default.py (4)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-429)
  • resolve_str (252-260)
  • resolve_str (263-271)
  • resolve_str (274-337)
src/draive/openai/images.py (1)
  • generate_image (21-60)
src/draive/models/generative.py (5)
  • completion (62-71)
  • completion (74-83)
  • completion (87-96)
  • completion (99-108)
  • completion (111-181)
src/draive/generation/text/state.py (2)
src/draive/models/tools/types.py (1)
  • Tool (76-104)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/multimodal/templates/repository.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (7)
  • Template (39-139)
  • TemplateDeclaration (143-199)
  • TemplateDefining (225-236)
  • TemplateLoading (213-221)
  • TemplateMissing (18-35)
  • of (56-84)
  • of (162-194)
src/draive/conversation/completion/state.py (2)
src/draive/models/tools/toolbox.py (1)
  • Toolbox (20-467)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/generation/text/types.py (2)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/conversation/completion/default.py (5)
src/draive/models/types.py (4)
  • ModelMemoryRecall (719-766)
  • ModelOutput (573-672)
  • ModelReasoning (518-562)
  • ModelToolRequest (311-353)
src/draive/multimodal/artifact.py (1)
  • ArtifactContent (11-96)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-429)
  • resolve_str (252-260)
  • resolve_str (263-271)
  • resolve_str (274-337)
src/draive/models/generative.py (6)
  • GenerativeModel (45-515)
  • loop (185-194)
  • loop (197-206)
  • loop (210-219)
  • loop (222-231)
  • loop (234-306)
tests/test_multimodal_template_variables.py (3)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/variables.py (3)
  • parse_template_variables (14-21)
  • resolve_multimodal_template (48-67)
  • resolve_text_template (24-45)
src/draive/multimodal/text.py (1)
  • TextContent (11-82)
src/draive/generation/image/state.py (1)
src/draive/multimodal/templates/types.py (1)
  • Template (39-139)
src/draive/multimodal/templates/types.py (2)
tests/test_toolbox.py (4)
  • meta (109-110)
  • meta (310-311)
  • description (97-98)
  • description (298-299)
src/draive/stages/stage.py (1)
  • with_meta (1390-1435)
src/draive/generation/model/default.py (3)
src/draive/multimodal/content.py (1)
  • MultimodalContent (25-592)
src/draive/multimodal/templates/types.py (2)
  • Template (39-139)
  • with_arguments (90-113)
src/draive/multimodal/templates/repository.py (4)
  • TemplatesRepository (61-429)
  • resolve_str (252-260)
  • resolve_str (263-271)
  • resolve_str (274-337)
🪛 LanguageTool
docs/guides/Templates.md

[style] ~104-~104: It’s more common nowadays to write this noun as one word.
Context: ...fining templates programmatically, pass variables={"user": "User name"} to document expected arguments. This...

(RECOMMENDED_COMPOUNDS)

🔇 Additional comments (42)
pyproject.toml (2)

8-8: Version bump is appropriate for the scope of changes.

The bump to 0.90.0 correctly reflects the major refactor replacing the Instructions subsystem with Templates throughout the codebase.


71-100: Tool configuration is properly set up for enforcing code quality standards.

Ruff (lines 71-84) and Pyright strict mode (lines 86-100) are configured to catch both style issues and type safety violations as intended by the coding guidelines.

Makefile (2)

10-10: Inconsistency between summary and code: .env include not removed.

The AI-generated summary claims the .env include was deleted, but Line 10 shows -include .env is still present and unchanged (no ~ marker). This is a discrepancy between the summary and the actual code state.


13-13: UV 0.9.4 is the current latest stable version and available for use.

UV 0.9.4 was released on Oct 17, 2025 as the latest stable version. Key improvements in this release include CUDA 13.0 support, auto-detection for Intel GPU on Windows, implementation of RFC 9457 HTTP error contexts display, and a bug fix for token obfuscation in uv auth token output. The patch-level bump from 0.9.0 to 0.9.4 carries minimal breaking change risk.

However, verify that this version aligns with your project's CI/CD pipeline requirements and any specific GPU or authentication workflows you depend on.

src/draive/helpers/instruction_refinement.py (7)

13-20: LGTM: Imports correctly updated for Templates subsystem.

The imports properly bring in the new Template types and repository while preserving ModelInstructions for the string content type.


27-41: LGTM: Function signature correctly updated.

The signature properly accepts a Template reference with optional pre-loaded instructions_content, allowing callers to avoid redundant loads while maintaining type safety.


135-158: LGTM: Elegant repository patching mechanism.

The patched_instructions_repository property correctly intercepts template loading to inject refined instructions while transparently delegating to the original repository for other templates. The implementation properly omits the defining parameter to use the default no-op, preventing modifications during evaluation.


422-423: LGTM: Correct context management.

The use of ctx.updated(...) correctly scopes the patched repository for evaluation, ensuring the refined instructions are used without affecting global state.


100-100: LGTM: Correct return type conversion.

Properly converts the final MultimodalContent result to string via to_str(), matching the ModelInstructions return type.


68-73: LGTM: Logging follows best practices.

All logging correctly uses ctx.log_* methods with structured, informative messages. No secrets or sensitive data are logged.

Also applies to: 99-99, 238-238, 242-242, 379-382, 420-421, 440-450, 561-562, 674-677


27-41: LGTM: Type annotations follow Python 3.12+ best practices.

Type hints are precise, use modern union syntax (|), and properly constrain generic parameters. The annotations provide clear contracts for the public API.

Also applies to: 103-113, 135-135

docs/guides/ComprehensiveEvaluation.md (1)

3-5: LGTM!

The formatting and wording improvements enhance clarity and readability without changing the technical content.

Also applies to: 9-10, 66-67

tests/test_toolbox.py (1)

14-14: LGTM! Import path updated correctly.

The import consolidation aligns with the broader refactoring to standardize on importing from draive.models.

src/draive/openai/responses.py (1)

70-89: LGTM! Import path consolidation is correct.

The addition of ModelReasoning to the draive.models import block aligns with the API surface consolidation effort.

src/draive/stages/stage.py (3)

39-39: LGTM! Template system imports added correctly.

The new imports support the migration from raw instruction strings to the Template system.

Also applies to: 49-49


287-372: LGTM! Template system integration in Stage.completion is correct.

The parameter type Template | ModelInstructions allows both template references and raw strings. The resolution via await TemplatesRepository.resolve_str(instructions) at line 356 correctly handles both cases.


374-463: LGTM! Template system integration is consistent across all completion methods.

All completion variants (prompting_completion, loopback_completion, result_completion) correctly:

  • Accept Template | ModelInstructions parameters
  • Resolve via await TemplatesRepository.resolve_str()
  • Pass resolved strings to GenerativeModel.loop()

Also applies to: 465-553, 555-622

mkdocs.yml (1)

101-101: LGTM! Templates guide added to navigation.

The new navigation entry correctly links to the Templates documentation.

tests/test_generative_merge.py (1)

1-1: Import consolidation is correct and complete.

Both ModelReasoning and ModelToolRequest are properly re-exported from draive.models in the __init__.py file and included in the __all__ tuple. The import path change is valid.

src/draive/postgres/memory.py (1)

9-15: LGTM! All imported symbols are properly re-exported from draive.models.

Verification confirms all five types (ModelContextElement, ModelInput, ModelMemory, ModelMemoryRecall, ModelOutput) are correctly imported from draive.models.types and listed in the module's __all__ tuple. The import consolidation improves readability with no issues.

src/draive/postgres/__init__.py (1)

12-12: LGTM! Clean migration from Instructions to Templates.

The import and export updates correctly reflect the transition from PostgresInstructionsRepository to PostgresTemplatesRepository, maintaining consistency with the broader Templates subsystem migration.

Also applies to: 23-23

src/draive/multimodal/__init__.py (1)

8-16: LGTM! Template symbols properly exposed.

The new imports and exports correctly expose the Templates subsystem through the multimodal package, aligning with the codebase's public API organization pattern.

Also applies to: 25-31

src/draive/generation/image/types.py (1)

3-4: LGTM! Protocol signature correctly updated.

The ImageGenerating protocol now accepts Template | ModelInstructions, properly reflecting the migration from the legacy ResolveableInstructions type. The imports are correct and consistent with the Templates subsystem.

Also applies to: 15-15

src/draive/multimodal/templates/__init__.py (1)

1-19: LGTM! Clean public API definition.

The module properly establishes the public API surface for the Templates subsystem, re-exporting all necessary types and protocols from the implementation modules.

docs/guides/Templates.md (1)

1-122: LGTM! Comprehensive and accurate documentation.

The Templates guide provides clear, well-structured documentation covering:

  • Template basics and immutability
  • Storage backends (volatile, file, Postgres, custom)
  • Runtime resolution with resolve and resolve_str
  • Template management and listing
  • Migration path from InstructionsRepository

The examples accurately reflect the implementation and the documentation will serve as a valuable reference for users transitioning to the Templates subsystem.

src/draive/generation/audio/types.py (1)

3-4: LGTM! Protocol signature correctly updated.

The AudioGenerating protocol now accepts Template | ModelInstructions, consistent with the Templates migration across all generation types. Imports are correct.

Also applies to: 15-15

src/draive/conversation/realtime/types.py (1)

12-13: LGTM! Protocol signature correctly updated.

The RealtimeConversationPreparing protocol now accepts Template | ModelInstructions for the instructions parameter, aligning with the Templates subsystem migration. The imports are correct and the change is consistent with other protocol updates across the codebase.

Also applies to: 130-130

tests/test_multimodal_template_variables.py (3)

12-86: LGTM! Comprehensive edge-case coverage for variable parsing.

The tests thoroughly validate parse_template_variables behavior across various edge cases including:

  • Unique variable deduplication
  • Handling of malformed placeholders (unclosed, unmatched, empty names)
  • Whitespace validation
  • Adjacent placeholders
  • Literal braces

88-127: LGTM! Thorough text template resolution tests.

The tests correctly validate text template resolution including:

  • Placeholder replacement
  • Repeated placeholder handling
  • Missing argument detection (raises KeyError)
  • Ignoring unused arguments (correct behavior per implementation)
  • Edge position handling

All calls correctly use keyword syntax (arguments=...) per the function signature.


129-207: LGTM! Excellent multimodal template resolution coverage.

The tests comprehensively validate multimodal template behavior:

  • Concatenation of mixed string and TextContent parts
  • Missing argument detection
  • Unused argument handling
  • Nested content flattening
  • Metadata boundary preservation (crucial for multimodal correctness)
  • Empty template handling

The metadata preservation test (lines 180-201) is particularly valuable for ensuring metadata boundaries are maintained during template resolution.

src/draive/generation/model/state.py (1)

23-30: LGTM — instruction union and overloads align with the new Templates API.
Types and conversions (MultimodalContent.of/Toolbox.of) look consistent.

Also applies to: 38-45, 53-60

src/draive/generation/model/types.py (1)

29-36: Protocol signature update looks correct.
Matches the refactor and downstream usage.

src/draive/generation/audio/default.py (1)

19-21: LGTM — type union for instructions matches the new API.
[supporting note only]

src/draive/generation/text/types.py (1)

15-20: LGTM — protocol now matches Template | ModelInstructions.

src/draive/generation/image/default.py (1)

19-21: LGTM — instruction union change is consistent.

src/draive/openai/images.py (1)

24-28: LGTM — signature updated to accept Template | ModelInstructions.

src/draive/conversation/completion/types.py (3)

26-33: LGTM — overload typing updated correctly.


38-45: LGTM — streaming overload matches non-streaming update.


49-55: LGTM — Protocol signature consistent with overloads.

src/draive/conversation/realtime/default.py (1)

39-44: Remove the entire review comment—the concern is based on an incorrect assumption.

The resolve_str function accepts template: Template | str, and the parameter instructions: Template | ModelInstructions expands to Template | str because ModelInstructions = str. The types match exactly, so there is no type contract violation. The code is type-safe and correct as-is.

Likely an incorrect or invalid review comment.

src/draive/__init__.py (2)

178-182: Top-level re-exports for Template family look good.

Consistent with the new Templates subsystem; import surface is clear.


350-354: all updated to include Template, TemplateDeclaration, TemplateMissing, TemplatesRepository.

Public API centralized as per guidelines; no duplicate entries observed.

Comment on lines +42 to +46
resolved_instructions = await TemplatesRepository.resolve_str(
instructions.with_arguments(
model_schema=generated.json_schema(indent=2),
)
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Forward **extra to all template resolution calls.

TemplatesRepository.resolve_str accepts **extra to allow backends (e.g., PostgreSQL, custom adapters) to receive contextual parameters such as revision hints, cache controls, or tenant identifiers. All three resolution paths ("full", "simplified", "skip") should forward **extra to enable backend context propagation.

Apply this diff:

                 else:
                     resolved_instructions = await TemplatesRepository.resolve_str(
                         instructions.with_arguments(
                             model_schema=generated.json_schema(indent=2),
-                        )
+                        ),
+                        **extra,
                     )
                 else:
                     resolved_instructions = await TemplatesRepository.resolve_str(
                         instructions.with_arguments(
                             model_schema=generated.simplified_schema(indent=2),
-                        )
+                        ),
+                        **extra,
                     )
             case "skip":  # instruction is not modified
-                resolved_instructions = await TemplatesRepository.resolve_str(instructions)
+                resolved_instructions = await TemplatesRepository.resolve_str(instructions, **extra)

Also applies to: 55-59, 62-62

🤖 Prompt for AI Agents
In src/draive/generation/model/default.py around lines 42-46 (also apply same
change at 55-59 and line 62), the TemplatesRepository.resolve_str calls need to
forward the **extra keyword args so backend adapters receive contextual
parameters; update each resolve_str invocation to pass **extra (e.g.,
TemplatesRepository.resolve_str(..., **extra)) and ensure the surrounding
function signature/stack provides an `extra` mapping to be forwarded unchanged
to these calls.

@KaQuMiQ KaQuMiQ merged commit c6720f4 into main Oct 21, 2025
2 checks passed
@KaQuMiQ KaQuMiQ deleted the feature/templates branch October 21, 2025 12:04
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.

1 participant