chore: update TinyHumans AI SDK to version 0.1.6 and add documentation for Rust SDK E2E tests

- Updated the TinyHumans AI SDK version in Cargo.lock and Cargo.toml.
- Added new documentation files for the Rust SDK E2E test run and TinyHumans AI SDK reference.
- Updated the skills subproject commit reference.
- Refactored memory client methods for improved functionality and consistency.
This commit is contained in:
M3gA-Mind
2026-03-27 17:36:02 +05:30
parent 861acc54c5
commit 749f825570
8 changed files with 1021 additions and 10 deletions
+165 -1
View File
@@ -615,6 +615,160 @@ async fn chat_send_inner(
skill_contexts.len()
);
// ── Step 2c: Query memory if recall context is insufficient ──────────
//
// Ask the LLM whether the recalled context is sufficient to answer the
// user. If not, it generates a targeted query and we call queryMemory
// to fetch extended context before building the final prompt.
let query_memory_context: Option<String> = if let Some(ref mem) = memory_client {
if cancel.is_cancelled() {
return Err("Request cancelled".to_string());
}
let recall_summary = memory_context.as_deref().unwrap_or("");
let skill_summary = skill_contexts.join("\n");
// Build the list of valid skill IDs for the LLM to choose from.
// "conversations" covers general conversation history.
let mut available_skills: Vec<String> = skill_ids.iter().cloned().collect();
available_skills.sort();
available_skills.push("conversations".to_string());
let skill_list = available_skills.join(", ");
let sufficiency_prompt = format!(
"You are a memory sufficiency checker. Given the recalled memory context and the \
user's question, decide if the recalled context contains enough specific information \
to answer the user.\n\n\
Recalled context:\n{recall_summary}\n{skill_summary}\n\n\
User message: {user_message}\n\n\
Available skill namespaces: {skill_list}\n\n\
Respond in JSON only:\n\
- If sufficient: {{\"needs_query\": false}}\n\
- If more context needed: {{\"needs_query\": true, \"skill_id\": \"<skill namespace that holds the relevant data>\", \"query\": \"<specific targeted question>\"}}"
);
let check_body = serde_json::json!({
"model": model,
"messages": [{"role": "user", "content": sufficiency_prompt}],
});
let check_url = format!("{}/openai/v1/chat/completions", backend_url);
log::info!("[chat] Step 2c: running memory sufficiency check");
log::info!(
"[chat] Step 2c request body: {}",
serde_json::to_string_pretty(&check_body).unwrap_or_default()
);
match tokio::time::timeout(
std::time::Duration::from_secs(30),
client
.post(&check_url)
.header("Authorization", format!("Bearer {}", auth_token))
.header("Content-Type", "application/json")
.json(&check_body)
.send(),
)
.await
{
Ok(Ok(resp)) if resp.status().is_success() => {
match resp.json::<ChatCompletionResponse>().await {
Ok(completion) => {
let content = completion
.choices
.first()
.and_then(|c| c.message.content.as_deref())
.unwrap_or("");
match serde_json::from_str::<serde_json::Value>(content) {
Ok(parsed) => {
if parsed.get("needs_query").and_then(|v| v.as_bool())
== Some(true)
{
if let Some(query) =
parsed.get("query").and_then(|v| v.as_str())
{
let skill_id = parsed
.get("skill_id")
.and_then(|v| v.as_str())
.unwrap_or("conversations");
log::info!(
"[chat] Sufficiency check: needs_query=true, skill_id={skill_id:?}, query={query:?}"
);
match mem
.query_skill_context(
skill_id,
thread_id,
query,
10,
)
.await
{
Ok(result) if !result.is_empty() => {
log::info!(
"[chat] queryMemory returned {} chars",
result.len()
);
Some(result)
}
Ok(_) => {
log::info!(
"[chat] queryMemory returned empty result"
);
None
}
Err(e) => {
log::warn!("[chat] queryMemory failed: {e}");
None
}
}
} else {
log::warn!(
"[chat] Sufficiency check: needs_query=true but no query field"
);
None
}
} else {
log::info!(
"[chat] Sufficiency check: recall context is sufficient"
);
None
}
}
Err(_) => {
log::warn!(
"[chat] Sufficiency check: failed to parse JSON response: {content}"
);
None
}
}
}
Err(e) => {
log::warn!("[chat] Sufficiency check: failed to parse inference response: {e}");
None
}
}
}
Ok(Ok(resp)) => {
log::warn!(
"[chat] Sufficiency check: inference returned HTTP {}",
resp.status()
);
None
}
Ok(Err(e)) => {
log::warn!("[chat] Sufficiency check: network error: {e}");
None
}
Err(_) => {
log::warn!("[chat] Sufficiency check: timed out after 30s, skipping queryMemory");
None
}
}
} else {
None
};
// ── Step 3: Build processed user message ────────────────────────────
let mut processed = user_message.to_string();
@@ -633,6 +787,12 @@ async fn chat_send_inner(
processed = format!("{}\n\n{}", skill_contexts.join("\n\n"), processed);
}
if let Some(ref qctx) = query_memory_context {
processed = format!(
"[QUERY_MEMORY_CONTEXT]\n{qctx}\n[/QUERY_MEMORY_CONTEXT]\n\n{processed}"
);
}
if let Some(ref notion) = notion_context {
processed = format!("{}\n\n{}", notion, processed);
}
@@ -699,7 +859,7 @@ async fn chat_send_inner(
loop_messages.len(),
url
);
log::debug!(
log::info!(
"[chat] Request body: {}",
serde_json::to_string_pretty(&request_body).unwrap_or_default()
);
@@ -1078,6 +1238,10 @@ async fn chat_send_mobile(
model,
messages.len()
);
log::info!(
"[chat] Request body: {}",
serde_json::to_string_pretty(&request_body).unwrap_or_default()
);
let response = tokio::select! {
_ = cancel.cancelled() => {
+5 -5
View File
@@ -85,7 +85,7 @@ impl MemoryClient {
let insert_resp = self
.inner
.insert_memory(InsertMemoryParams {
document_id: Some(document_id_final),
document_id: document_id_final,
title: title.to_string(),
content: content.to_string(),
namespace: namespace.clone(),
@@ -118,7 +118,7 @@ impl MemoryClient {
loop {
tokio::time::sleep(std::time::Duration::from_secs(30)).await;
match self.inner.ingestion_job_status(&job_id).await {
match self.inner.get_ingestion_job(&job_id).await {
Ok(status_resp) => {
let state = status_resp
.data
@@ -241,7 +241,7 @@ impl MemoryClient {
/// List all ingested memory documents as returned by the API.
pub async fn list_documents(&self) -> Result<serde_json::Value, String> {
self.inner
.list_documents()
.list_documents(tinyhumansai::ListDocumentsParams::default())
.await
.map_err(|e| format!("Memory list documents failed: {e}"))
}
@@ -295,9 +295,9 @@ impl MemoryClient {
pub async fn clear_skill_memory(
&self,
skill_id: &str,
integration_id: &str,
_integration_id: &str,
) -> Result<(), String> {
let namespace = format!("skill:{skill_id}:{integration_id}");
let namespace = skill_id.to_string();
log::info!("[memory] clear_skill_memory: entry (namespace={namespace})");
log::debug!("[memory] clear_skill_memory: payload → namespace={namespace}");
let result = self.inner