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:
@ -57,9 +57,7 @@ async fn drain_request(stream: &mut tokio::net::TcpStream) {
|
||||
}
|
||||
|
||||
fn find_subslice(haystack: &[u8], needle: &[u8]) -> Option<usize> {
|
||||
haystack
|
||||
.windows(needle.len())
|
||||
.position(|w| w == needle)
|
||||
haystack.windows(needle.len()).position(|w| w == needle)
|
||||
}
|
||||
|
||||
/// Builds an HTTP `200 OK` response with a JSON body and the right `Content-Length`.
|
||||
@ -98,17 +96,22 @@ async fn http_embedder_parses_vectors_and_restores_input_order() {
|
||||
.embed(&["first".to_string(), "second".to_string()])
|
||||
.await
|
||||
.expect("embed must succeed");
|
||||
assert_eq!(out, vec![vec![1.0, 0.0], vec![0.0, 1.0]], "input order restored by index");
|
||||
assert_eq!(
|
||||
out,
|
||||
vec![vec![1.0, 0.0], vec![0.0, 1.0]],
|
||||
"input order restored by index"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn http_embedder_empty_input_short_circuits_without_network() {
|
||||
// A closed/never-bound endpoint: no request must be made for empty input.
|
||||
let embedder = HttpEmbedder::from_profile(&local_server_profile(
|
||||
"http://127.0.0.1:1/v1/embeddings",
|
||||
4,
|
||||
));
|
||||
let out = embedder.embed(&[]).await.expect("empty input ⇒ empty output, no I/O");
|
||||
let embedder =
|
||||
HttpEmbedder::from_profile(&local_server_profile("http://127.0.0.1:1/v1/embeddings", 4));
|
||||
let out = embedder
|
||||
.embed(&[])
|
||||
.await
|
||||
.expect("empty input ⇒ empty output, no I/O");
|
||||
assert!(out.is_empty());
|
||||
}
|
||||
|
||||
@ -124,10 +127,8 @@ async fn http_embedder_non_2xx_is_unavailable() {
|
||||
#[tokio::test]
|
||||
async fn http_embedder_unreachable_host_is_unavailable() {
|
||||
// Port 1 on loopback: nothing listens ⇒ connection refused ⇒ Unavailable.
|
||||
let embedder = HttpEmbedder::from_profile(&local_server_profile(
|
||||
"http://127.0.0.1:1/v1/embeddings",
|
||||
2,
|
||||
));
|
||||
let embedder =
|
||||
HttpEmbedder::from_profile(&local_server_profile("http://127.0.0.1:1/v1/embeddings", 2));
|
||||
let err = embedder.embed(&["x".to_string()]).await.unwrap_err();
|
||||
assert!(matches!(err, EmbedderError::Unavailable(_)), "got {err:?}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user