feat(memory): config embedders (LOT C2) + suggestion contextuelle (LOT C3) + contexte projet partagé
- LOT C2 (§14.5.3) : use cases de configuration des embedders déclaratifs (List/Save/Delete + DescribeEmbedderEngines : modèles ONNX recommandés, environnement local détecté, stratégies compilées). UI EmbedderSettings. - LOT C3 (§14.5.5) : suggestion contextuelle best-effort à l'activation quand la mémoire dépasse le budget de recall sans embedder configuré (event EmbedderSuggested, anti-spam 1×/session, « ne plus demander »). - Contexte projet partagé .ideai/CONTEXT.md (model-agnostic) injecté à tous les agents/profils au lancement, avant la persona. UI ProjectContextPanel. Tests : backend workspace vert (0 échec) ; frontend 306/306. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -32,7 +32,7 @@ use crate::events::DomainEvent;
|
||||
use crate::ids::{AgentId, SessionId};
|
||||
use crate::markdown::MarkdownDoc;
|
||||
use crate::memory::{Memory, MemoryIndexEntry, MemoryLink, MemorySlug};
|
||||
use crate::profile::AgentProfile;
|
||||
use crate::profile::{AgentProfile, EmbedderProfile};
|
||||
use crate::project::{Project, ProjectPath};
|
||||
use crate::remote::RemoteKind;
|
||||
use crate::skill::{Skill, SkillScope};
|
||||
@ -798,6 +798,94 @@ pub trait ProfileStore: Send + Sync {
|
||||
async fn mark_configured(&self) -> Result<(), StoreError>;
|
||||
}
|
||||
|
||||
/// CRUD for the declarative [`EmbedderProfile`]s in the global IDE store
|
||||
/// (`embedder.json`, LOT C, §14.5.3). Mirrors [`ProfileStore`] for the embedding
|
||||
/// engines: adding an engine is *data* the user configures, not code.
|
||||
///
|
||||
/// There is **no** `is_configured`/`mark_configured`: the default embedder posture
|
||||
/// is `none` **by absence of the file** (an empty/missing `embedder.json` ⇒ recall
|
||||
/// stays the dependency-free naïve étage 1). A configured embedder takes effect at
|
||||
/// the **next IDE start** (the composition root freezes the recall for the session).
|
||||
#[async_trait]
|
||||
pub trait EmbedderProfileStore: Send + Sync {
|
||||
/// Lists all configured embedder profiles (empty when none configured).
|
||||
///
|
||||
/// # Errors
|
||||
/// [`StoreError`] on failure.
|
||||
async fn list(&self) -> Result<Vec<EmbedderProfile>, StoreError>;
|
||||
|
||||
/// Saves (creates or replaces by id) an embedder profile.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`StoreError`] on failure.
|
||||
async fn save(&self, profile: &EmbedderProfile) -> Result<(), StoreError>;
|
||||
|
||||
/// Deletes an embedder profile by id.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`StoreError::NotFound`] if no profile carries that id.
|
||||
async fn delete(&self, id: &str) -> Result<(), StoreError>;
|
||||
}
|
||||
|
||||
/// Best-effort snapshot of the embedding *environment*: which local engines are
|
||||
/// already installed/cached on this machine. Drives the "configure an embedder?"
|
||||
/// UI (C2/C3) so it can detect an existing engine before suggesting a download.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct EmbedderEnvReport {
|
||||
/// Whether an Ollama-style local embedding server was detected (best-effort;
|
||||
/// always `false` in a build without the HTTP capability).
|
||||
pub ollama_detected: bool,
|
||||
/// Ids of the recommended ONNX models already present in the local cache.
|
||||
pub onnx_cached_models: Vec<String>,
|
||||
}
|
||||
|
||||
/// Probes the local embedding environment (installed/cached engines). **Best-effort
|
||||
/// by contract**: [`inspect`](Self::inspect) never errors — an unreachable host or
|
||||
/// an unreadable cache simply yields a report with the corresponding fields unset.
|
||||
#[async_trait]
|
||||
pub trait EmbedderEnvInspector: Send + Sync {
|
||||
/// Returns a best-effort snapshot of the local embedding environment.
|
||||
async fn inspect(&self) -> EmbedderEnvReport;
|
||||
}
|
||||
|
||||
/// The persisted user response to the "configure an embedder?" suggestion
|
||||
/// (ARCHITECTURE §14.5.5, LOT C3), stored per project under
|
||||
/// `.ideai/memory/.embedder-prompt.json`. Absence of the file ⇒ never answered.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum EmbedderPromptDismissal {
|
||||
/// "Plus tard" — re-proposable on a future session (not this one).
|
||||
Later,
|
||||
/// "Ne plus demander" — never publish the suggestion again (persistent).
|
||||
Never,
|
||||
}
|
||||
|
||||
/// Persistence of the per-project embedder-suggestion state
|
||||
/// (`.ideai/memory/.embedder-prompt.json`, LOT C3). A fine-grained port (Interface
|
||||
/// Segregation) so the suggestion use cases depend on this alone, not on a wider
|
||||
/// store. **Best-effort reads**: a missing/malformed file is *not* an error — it
|
||||
/// reads as "never answered" ([`None`]).
|
||||
#[async_trait]
|
||||
pub trait EmbedderPromptStore: Send + Sync {
|
||||
/// Reads the recorded dismissal for `root`'s project, or `None` when the user
|
||||
/// has never answered (no file / unreadable / malformed).
|
||||
///
|
||||
/// # Errors
|
||||
/// [`StoreError`] only on an unexpected I/O failure the adapter chooses to
|
||||
/// surface; a plain missing file degrades to `Ok(None)`.
|
||||
async fn read(&self, root: &ProjectPath)
|
||||
-> Result<Option<EmbedderPromptDismissal>, StoreError>;
|
||||
|
||||
/// Records the user's dismissal choice for `root`'s project.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`StoreError`] on a persistence failure.
|
||||
async fn write(
|
||||
&self,
|
||||
root: &ProjectPath,
|
||||
dismissal: EmbedderPromptDismissal,
|
||||
) -> Result<(), StoreError>;
|
||||
}
|
||||
|
||||
/// Reads/writes agent `.md` contexts and the project manifest, within a project.
|
||||
#[async_trait]
|
||||
pub trait AgentContextStore: Send + Sync {
|
||||
@ -895,8 +983,7 @@ pub trait GitPort: Send + Sync {
|
||||
///
|
||||
/// # Errors
|
||||
/// [`GitError`] on failure.
|
||||
async fn log(&self, root: &ProjectPath, limit: usize)
|
||||
-> Result<Vec<GitCommitInfo>, GitError>;
|
||||
async fn log(&self, root: &ProjectPath, limit: usize) -> Result<Vec<GitCommitInfo>, GitError>;
|
||||
|
||||
/// Returns the commit graph (all local branches, topological + time sort).
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user