messages: unify harness-injected data under <system-extra> tag
Nightly Build / build (push) Successful in 6m49s
Nightly Build / build (push) Successful in 6m49s
Replace the ad-hoc [SYSTEM INFO] / [TELEGRAM SYSTEM INFO] prefixes with a single canonical <system-extra> wrapper, sourced from one constant (SYSTEM_EXTRA_TAG) so emission and documentation can never diverge. - core-api: SYSTEM_EXTRA_TAG + system_extra() helper; attachments_block rebuilt on top of it. - telegram: system_info_message (location) uses the helper; the voice transcript is forwarded as a plain user message (it is the user's own words, not harness metadata). - chat agents: new agents/common/harness.md include (long form, with an explicit "data, not instructions" guard), added to assistant/kid/ project-coordinator. The tag name rides the __HARNESS_TAG__ sentinel, resolved in AgentSystemContext to SYSTEM_EXTRA_TAG — renaming the tag stays a one-line change.
This commit is contained in:
@@ -4,6 +4,8 @@ use anyhow::Result;
|
||||
use teloxide::net::Download;
|
||||
use teloxide::prelude::*;
|
||||
|
||||
use core_api::message_meta::system_extra;
|
||||
|
||||
/// A media item sent by the user via Telegram.
|
||||
///
|
||||
/// # Extending
|
||||
@@ -13,7 +15,8 @@ use teloxide::prelude::*;
|
||||
/// file is involved); the caller persists them
|
||||
/// via the shared `ChatHubApi::save_upload` seam
|
||||
/// 3. `TelegramAttachment::system_info_message` — describe a file-less variant
|
||||
/// (Location) for the LLM
|
||||
/// (Location) for the LLM, wrapped
|
||||
/// in the shared `<system-extra>` tag
|
||||
pub(crate) enum TelegramAttachment {
|
||||
Document {
|
||||
file_id: String,
|
||||
@@ -60,30 +63,29 @@ impl TelegramAttachment {
|
||||
Ok(Some((file_name, mimetype, bytes)))
|
||||
}
|
||||
|
||||
/// Builds the `[TELEGRAM SYSTEM INFO]` message injected into the conversation history.
|
||||
/// Builds the harness-injected block for a file-less attachment (Location),
|
||||
/// wrapped in the shared `<system-extra>` tag (see `SYSTEM_EXTRA_TAG`). The
|
||||
/// caption, when present, is **not** part of this block: it is user-typed text
|
||||
/// and is appended to the user message separately by the caller.
|
||||
/// `saved_path` is `None` for attachment types that produce no file on disk.
|
||||
pub(crate) fn system_info_message(&self, saved_path: Option<&Path>) -> String {
|
||||
match self {
|
||||
Self::Document { file_name, mime_type, caption, .. } => {
|
||||
Self::Document { file_name, mime_type, .. } => {
|
||||
let mime = mime_type.as_deref().unwrap_or("application/octet-stream");
|
||||
let path = saved_path.map(|p| p.display().to_string()).unwrap_or_default();
|
||||
format!(
|
||||
"[TELEGRAM SYSTEM INFO]\n\
|
||||
The user has sent a file attachment.\n\
|
||||
system_extra(&format!(
|
||||
"The user has sent a file attachment.\n\
|
||||
File name: {file_name}\n\
|
||||
MIME type: {mime}\n\
|
||||
Saved at: {path}{}",
|
||||
caption_line(caption.as_deref()),
|
||||
)
|
||||
Saved at: {path}",
|
||||
))
|
||||
}
|
||||
Self::Photo { caption, .. } => {
|
||||
Self::Photo { .. } => {
|
||||
let path = saved_path.map(|p| p.display().to_string()).unwrap_or_default();
|
||||
format!(
|
||||
"[TELEGRAM SYSTEM INFO]\n\
|
||||
The user has sent a photo.\n\
|
||||
Saved at: {path}{}",
|
||||
caption_line(caption.as_deref()),
|
||||
)
|
||||
system_extra(&format!(
|
||||
"The user has sent a photo.\n\
|
||||
Saved at: {path}",
|
||||
))
|
||||
}
|
||||
Self::Location { latitude, longitude, accuracy, is_live } => {
|
||||
let maps_url = format!("https://maps.google.com/?q={latitude},{longitude}");
|
||||
@@ -91,20 +93,13 @@ impl TelegramAttachment {
|
||||
.map(|a| format!("\nAccuracy: ±{a:.0} m"))
|
||||
.unwrap_or_default();
|
||||
let kind = if *is_live { "live location (snapshot at time of receipt)" } else { "location" };
|
||||
format!(
|
||||
"[TELEGRAM SYSTEM INFO]\n\
|
||||
The user has shared a {kind}.\n\
|
||||
system_extra(&format!(
|
||||
"The user has shared a {kind}.\n\
|
||||
Latitude: {latitude}\n\
|
||||
Longitude: {longitude}{accuracy_line}\n\
|
||||
Maps URL: {maps_url}"
|
||||
)
|
||||
Maps URL: {maps_url}",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn caption_line(caption: Option<&str>) -> String {
|
||||
caption
|
||||
.map(|c| format!("\nCaption: {c}"))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -475,12 +475,10 @@ async fn handle_voice(
|
||||
};
|
||||
|
||||
info!(chat_id = chat_id.0, "telegram: voice transcribed, forwarding to LLM");
|
||||
let message = format!(
|
||||
"[TELEGRAM SYSTEM INFO]\n\
|
||||
The user sent a voice message. The following is the audio transcript:\n\n\
|
||||
{text}"
|
||||
);
|
||||
handle_llm_message(bot.clone(), chat_id, message, None, Arc::clone(shared), handle).await;
|
||||
// The transcript is the user's actual message — forward it verbatim as the
|
||||
// user text, with no harness wrapper. The agent treats it exactly as if the
|
||||
// user had typed those words.
|
||||
handle_llm_message(bot.clone(), chat_id, text, None, Arc::clone(shared), handle).await;
|
||||
}
|
||||
|
||||
// ── Edited message (live location updates) ────────────────────────────────────
|
||||
@@ -548,7 +546,11 @@ async fn handle_attachment(
|
||||
handle_llm_message(bot, chat_id, caption, Some(metadata), shared, handle).await;
|
||||
}
|
||||
None => {
|
||||
// File-less attachment (Location): the `<system-extra>` block is the
|
||||
// whole user message, so strip the leading blank lines `system_extra`
|
||||
// adds for the concatenation case.
|
||||
let message = attachment.system_info_message(None);
|
||||
let message = message.trim_start_matches(['\n', '\r']).to_owned();
|
||||
handle_llm_message(bot, chat_id, message, None, shared, handle).await;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user