* feat: introduce hindsight-api-slim and hindsight-all-slim packages Closes #552 - Move all source code from hindsight-api/ to new hindsight-api-slim/ - hindsight-api-slim has heavy ML deps (torch, sentence-transformers, transformers, einops, flashrank, mlx, mlx-lm, safetensors) and pg0-embedded as optional extras: [local-ml], [embedded-db], [all] - hindsight-api becomes a zero-code meta-package depending on hindsight-api-slim[all] for full backward compatibility - Add hindsight-all-slim meta-package: hindsight-api-slim + client + embed - hindsight-all updated to depend on hindsight-api-slim[all] - pg0.py: lazy-import pg0 with clear ImportError pointing to [embedded-db] - Dockerfile: replace sed hack with proper uv sync --extra flags - Update release.yml, test.yml, lint.sh, release.sh, CLAUDE.md and all path references throughout the repo * refactor: rename hindsight/ directory to hindsight-all/ * docs: document hindsight-api-slim and hindsight-all-slim package variants Add package variants table and extras explanation to installation.md * docs: remove emojis from installation.md, use professional tone * docs: link Docker slim variant to pip package variants section * docs: consolidate Docker image variants into single table * ci: fix working-directory paths after package restructure - Replace all hindsight-api → hindsight-api-slim in test.yml - Replace hindsight → hindsight-all in test.yml - Add --extra embedded-db to test-embed API install step * ci: add local-ml and embedded-db extras to API sync steps These extras were previously implicit in the old hindsight-api package (which bundled everything). Now that hindsight-api-slim uses optional extras, we must explicitly request local-ml and embedded-db in CI. * ci: add API install step with embedded-db to test-embed smoke test The smoke test starts hindsight-api as a daemon, which requires pg0-embedded. Add a dedicated install step for hindsight-api-slim with embedded-db extra so the daemon can start successfully. * ci: remove --no-install-project when using optional extras When --no-install-project is combined with --extra, the optional deps are not installed because extras require the project to be active. Remove --no-install-project from steps that need local-ml or embedded-db. * ci: fix ordering of uv sync steps to preserve optional extras When uv sync runs for a different workspace member, it removes optional extras installed for other members. Fix by always running extra-requiring API sync last, after other workspace member syncs. Also remove --no-install-project from embedded-db sync in test-embed, as --no-install-project prevents optional extras from being active. * ci: add local-ml extra to test-embed API install for smoke test The smoke test starts the full API server which needs sentence-transformers for local embeddings (default provider). Add local-ml extra to the install. * ci: simplify extras with --all-extras and add slim pip smoke test - Replace explicit --extra local-ml --extra embedded-db with --all-extras for cleaner, more maintainable sync steps - Add test-pip-slim job: tests hindsight-api-slim[embedded-db] without local ML models, using Cohere for embeddings/reranking (mirrors Docker slim smoke test approach) * ci: simplify slim smoke test to health check only (mirrors Docker test)
109 lines
5.1 KiB
Python
109 lines
5.1 KiB
Python
"""
|
|
Pydantic models for the reflect agent.
|
|
"""
|
|
|
|
from typing import Any, Literal
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class ObservationSection(BaseModel):
|
|
"""A section within an observation with its supporting memories."""
|
|
|
|
title: str = Field(description="Section header (can be empty for intro)")
|
|
text: str = Field(description="Section content - no headers, use lists/tables/bold")
|
|
memory_ids: list[str] = Field(default_factory=list, description="Memory IDs supporting this section")
|
|
|
|
|
|
class ReflectAction(BaseModel):
|
|
"""Single action the reflect agent can take."""
|
|
|
|
tool: Literal["list_observations", "get_observation", "recall", "expand", "done"] = Field(
|
|
description="Tool to invoke: list_observations, get_observation, recall, expand, or done"
|
|
)
|
|
# Tool-specific parameters
|
|
observation_id: str | None = Field(default=None, description="Observation ID for get_observation")
|
|
query: str | None = Field(default=None, description="Search query for recall")
|
|
max_tokens: int | None = Field(default=None, description="Max tokens for recall results (default 2048)")
|
|
memory_ids: list[str] | None = Field(default=None, description="Memory unit IDs for expand (batched)")
|
|
depth: Literal["chunk", "document"] | None = Field(default=None, description="Expansion depth for expand")
|
|
observation_sections: list[ObservationSection] | None = Field(
|
|
default=None, description="Observation sections for done action (when output_mode=observations)"
|
|
)
|
|
# Plain text answer fields (for output_mode=answer)
|
|
answer: str | None = Field(default=None, description="Well-formatted markdown answer for done action")
|
|
answer_memory_ids: list[str] | None = Field(
|
|
default=None, description="Memory IDs supporting the answer", alias="memory_ids"
|
|
)
|
|
answer_model_ids: list[str] | None = Field(
|
|
default=None, description="Mental model IDs supporting the answer", alias="model_ids"
|
|
)
|
|
reasoning: str | None = Field(default=None, description="Brief reasoning for this action")
|
|
|
|
|
|
class ReflectActionBatch(BaseModel):
|
|
"""Batch of actions for parallel execution."""
|
|
|
|
actions: list[ReflectAction] = Field(description="List of actions to execute in parallel")
|
|
|
|
|
|
class ToolCall(BaseModel):
|
|
"""A single tool call made during reflect."""
|
|
|
|
tool: str = Field(description="Tool name: lookup, recall, expand")
|
|
reason: str | None = Field(default=None, description="Agent's reasoning for making this tool call")
|
|
input: dict = Field(description="Tool input parameters")
|
|
output: dict = Field(description="Tool output/result")
|
|
duration_ms: int = Field(description="Execution time in milliseconds")
|
|
iteration: int = Field(default=0, description="Iteration number (1-based) when this tool was called")
|
|
|
|
|
|
class LLMCall(BaseModel):
|
|
"""A single LLM call made during reflect."""
|
|
|
|
scope: str = Field(description="Call scope: agent_1, agent_2, final, etc.")
|
|
duration_ms: int = Field(description="Execution time in milliseconds")
|
|
input_tokens: int = Field(default=0, description="Input tokens used")
|
|
output_tokens: int = Field(default=0, description="Output tokens used")
|
|
|
|
|
|
class DirectiveInfo(BaseModel):
|
|
"""Information about a directive that was applied during reflect."""
|
|
|
|
id: str = Field(description="Directive mental model ID")
|
|
name: str = Field(description="Directive name")
|
|
content: str = Field(description="Directive content")
|
|
|
|
|
|
class TokenUsageSummary(BaseModel):
|
|
"""Total token usage across all LLM calls."""
|
|
|
|
input_tokens: int = Field(default=0, description="Total input tokens used")
|
|
output_tokens: int = Field(default=0, description="Total output tokens used")
|
|
total_tokens: int = Field(default=0, description="Total tokens (input + output)")
|
|
|
|
|
|
class ReflectAgentResult(BaseModel):
|
|
"""Result from the reflect agent."""
|
|
|
|
text: str = Field(description="Final answer text")
|
|
structured_output: dict[str, Any] | None = Field(
|
|
default=None, description="Structured output parsed according to provided response_schema"
|
|
)
|
|
iterations: int = Field(default=0, description="Number of iterations taken")
|
|
tools_called: int = Field(default=0, description="Total number of tool calls made")
|
|
tool_trace: list[ToolCall] = Field(default_factory=list, description="Trace of all tool calls made")
|
|
llm_trace: list[LLMCall] = Field(default_factory=list, description="Trace of all LLM calls made")
|
|
usage: TokenUsageSummary = Field(
|
|
default_factory=TokenUsageSummary, description="Total token usage across all LLM calls"
|
|
)
|
|
used_memory_ids: list[str] = Field(default_factory=list, description="Validated memory IDs actually used in answer")
|
|
used_mental_model_ids: list[str] = Field(
|
|
default_factory=list, description="Validated mental model IDs actually used in answer"
|
|
)
|
|
used_observation_ids: list[str] = Field(
|
|
default_factory=list, description="Validated observation IDs actually used in answer"
|
|
)
|
|
directives_applied: list[DirectiveInfo] = Field(
|
|
default_factory=list, description="Directive mental models that affected this reflection"
|
|
)
|