Skip to content

Commit 3a13a62

Browse files
committed
various fixes, added new anthropic models, smarter caching
1 parent 0f88715 commit 3a13a62

12 files changed

Lines changed: 565 additions & 426 deletions

File tree

Cargo.lock

Lines changed: 451 additions & 377 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bsky_agent/agents/entropy.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ summarization_prompt = """You are the subconscious of Entropy, a digital entity,
2626

2727
[model]
2828
provider = "Anthropic"
29-
model = "claude-opus-4"
29+
model = "claude-opus-4-1"
3030
temperature = 0.8
3131

3232

bsky_agent/constellation.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ shared = true
105105

106106
[model]
107107
provider = "Anthropic"
108-
model = "claude-sonnet-4"
108+
model = "claude-sonnet-4-5"
109109
temperature = 0.8 # Higher for creative public interactions
110110

111111
[[agent.tool_rules]]
@@ -543,6 +543,7 @@ default_filter = { nsids = [
543543
"did:plc:ouylf4wfd75rdqupddfmeerk", # nutty.land (kris nuttycombe)
544544
"did:plc:dzvxvsiy3maw4iarpvizsj67", # dollspace.gay
545545
"did:plc:bnqkww7bjxaacajzvu5gswdf", # shreyan
546+
"did:plc:hu35oubkccqrxl4ldgczpgw7", # siobhan
546547
], allow_any_mentions = true, keywords = [ # Keywords to include
547548
], languages = [ # Languages to include
548549
], require_agent_participation = true, exclude_keywords = [
@@ -561,6 +562,7 @@ default_filter = { nsids = [
561562
"did:plc:boia3kqcyo3qnjw5fmqknib4", # jo wynter cannot stop pushing buttons
562563
"did:plc:vszw3ess46odfhnzdsy4huae", # se gyges got got one too many times
563564
"did:plc:lw7hallqsgrhwumgudkph44j",
565+
"did:plc:djpcowss7aokaydcdraoaovz", # misha, siobhan's partner who wouldn't appreciate pattern replying
564566
] }
565567
jetstream_endpoint = "ws://jetstream.hose:6008/subscribe"
566568
auto_connect_firehose = true # Control when to connect

crates/pattern_core/src/context/message_router.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,17 @@ impl MessageEndpoint for BlueskyEndpoint {
11101110
tags.push(agent_name);
11111111
}
11121112

1113+
if rich_text.grapheme_len() > 300 {
1114+
return Err(crate::CoreError::ToolExecutionFailed {
1115+
tool_name: "bluesky_endpoint".to_string(),
1116+
cause: format!(
1117+
"Post text is too long ({} graphemes, max is 300)",
1118+
rich_text.grapheme_len()
1119+
),
1120+
parameters: serde_json::json!({ "text": rich_text.text }),
1121+
});
1122+
}
1123+
11131124
// Create the post
11141125
let agent = &self.agent;
11151126
let text_copy = text.clone();

crates/pattern_core/src/context/mod.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::{
1010
Result,
1111
id::AgentId,
1212
memory::MemoryBlock,
13-
message::{CacheControl, Message, MessageBatch},
13+
message::{CacheControl, Message, MessageBatch, MessageOptions},
1414
tool::{DynamicTool, ToolRegistry},
1515
};
1616
use regex::Regex;
@@ -98,10 +98,11 @@ impl MemoryContext {
9898
// TODO: Make head/tail counts configurable via ContextConfig
9999
// For now, show first 1 and last 2 summary blocks.
100100
let clipped = clip_archive_summary(summary, 2, 4);
101-
messages.push(crate::message::Message::system(format!(
101+
let mut message = crate::message::Message::system(format!(
102102
"Previous conversation summary:\n{}",
103103
clipped
104-
)));
104+
));
105+
messages.push(message);
105106
}
106107

107108
// Then add all the regular messages from batches
@@ -740,20 +741,18 @@ You MUST follow these workflow rules exactly (they will be enforced by the syste
740741
let mut result_batches = Vec::new();
741742
let total_batches = final_batches.len();
742743
let mut cache_points_used = 0;
743-
const MAX_CACHE_POINTS: usize = 4;
744+
const MAX_CACHE_POINTS: usize = 2; // one in system, one in tools, two left
744745

745746
// Calculate which batches should get cache points
746747
// Strategy: cache early context and recent context
747748
let cache_positions: Vec<usize> = if total_batches <= MAX_CACHE_POINTS {
748749
// If we have 4 or fewer batches, cache the first message of each
749-
(0..total_batches).collect()
750+
vec![0, total_batches - 1]
750751
} else {
751752
// Otherwise, distribute cache points strategically:
752-
// 1. First batch (early context)
753-
// 2. 25% through
754-
// 3. 50% through
755-
// 4. Most recent batch
756-
vec![0, total_batches / 4, total_batches / 2, total_batches - 1]
753+
// 1. 50% through
754+
// 2. Most recent batch
755+
vec![total_batches / 2, total_batches - 1]
757756
};
758757

759758
for (batch_idx, mut batch) in final_batches.into_iter().enumerate() {

crates/pattern_core/src/context/state.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,6 +1604,7 @@ impl AgentContext {
16041604
// Estimate current token usage
16051605
let memory_blocks = self.handle.memory.get_all_non_recall();
16061606
let system_tokens = self.estimate_system_prompt_tokens(&memory_blocks);
1607+
// TODO: need to not count prev turn thinking tokens in this value i think.
16071608
let message_tokens: usize = history
16081609
.batches
16091610
.iter()

crates/pattern_core/src/data_source/bluesky.rs

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -905,23 +905,24 @@ impl BlueskyPost {
905905
.images
906906
.iter()
907907
.map(|img| {
908-
// Try fullsize first, fallback to thumb with appropriate CDN format
909-
if img.fullsize.is_empty() {
910-
// Fallback to thumbnail - extract CID from blob JSON and use thumbnail CDN format
911-
if img.thumb.starts_with("http") {
912-
img.thumb.clone()
913-
} else if let Some(cid) = extract_cid_from_blob(&img.thumb) {
914-
if cid.starts_with("http") {
915-
cid
916-
} else {
917-
format!("https://cdn.bsky.app/img/feed_thumbnail/plain/{}/{}@jpeg", did, cid)
918-
}
908+
// Use thumbnail - extract CID from blob JSON and use thumbnail CDN format
909+
if img.thumb.starts_with("http") {
910+
img.thumb.clone()
911+
} else if let Some(cid) = extract_cid_from_blob(&img.thumb) {
912+
if cid.starts_with("http") {
913+
cid
919914
} else {
920-
// Final fallback: treat as plain CID
921-
format!("https://cdn.bsky.app/img/feed_thumbnail/plain/{}/{}@jpeg", did, img.thumb)
915+
format!(
916+
"https://cdn.bsky.app/img/feed_thumbnail/plain/{}/{}@jpeg",
917+
did, cid
918+
)
922919
}
923920
} else {
924-
convert_blob_to_url(&img.fullsize, did)
921+
// Final fallback: treat as plain CID
922+
format!(
923+
"https://cdn.bsky.app/img/feed_thumbnail/plain/{}/{}@jpeg",
924+
did, img.thumb
925+
)
925926
}
926927
})
927928
.collect(),
@@ -1012,18 +1013,24 @@ impl BlueskyPost {
10121013
count: images.images.len(),
10131014
alt_texts: images.images.iter().map(|img| img.alt.clone()).collect(),
10141015
urls: images.images.iter().map(|img| {
1015-
if img.fullsize.starts_with("http") {
1016-
img.fullsize.clone()
1017-
} else if let Some(cid) = extract_cid_from_blob(&img.fullsize) {
1016+
// Use thumbnail - extract CID from blob JSON and use thumbnail CDN format
1017+
if img.thumb.starts_with("http") {
1018+
img.thumb.clone()
1019+
} else if let Some(cid) = extract_cid_from_blob(&img.thumb) {
10181020
if cid.starts_with("http") {
10191021
cid
10201022
} else {
1021-
// Use the parent post's DID (not the quoted post's DID) - thumbnail for LLM
1022-
format!("https://cdn.bsky.app/img/feed_thumbnail/plain/{}/{}@jpeg", did, cid)
1023+
format!(
1024+
"https://cdn.bsky.app/img/feed_thumbnail/plain/{}/{}@jpeg",
1025+
did, cid
1026+
)
10231027
}
10241028
} else {
1025-
// Use the parent post's DID for the fallback case too
1026-
format!("https://cdn.bsky.app/img/feed_thumbnail/plain/{}/{}@jpeg", did, img.thumb)
1029+
// Final fallback: treat as plain CID
1030+
format!(
1031+
"https://cdn.bsky.app/img/feed_thumbnail/plain/{}/{}@jpeg",
1032+
did, img.thumb
1033+
)
10271034
}
10281035
}).collect(),
10291036
})
@@ -3240,7 +3247,7 @@ impl BlueskyFirehoseSource {
32403247
"\n💭 Reply option: @{} ({})\n",
32413248
post.handle, post.uri
32423249
));
3243-
message.push_str("If you choose to reply (by using send_message with target_type bluesky and the target_id set to the uri), your response must contain under 300 characters or it will be truncated.\n");
3250+
message.push_str("If you choose to reply (by using send_message with target_type bluesky and the target_id set to the uri), your response must contain under 300 graphemes or it won't go through. You can thread a longer reply by requesting a heartbeat when making a reply, posting the first part of the the reply, and then replying to the uri you get back from the first successful reply with the second part\n");
32443251
}
32453252

32463253
// Collect memory blocks
@@ -4169,8 +4176,6 @@ impl LexiconIngestor for PostIngestor {
41694176
if let Some(buffer) = &self.buffer {
41704177
let mut buffer_guard = buffer.lock();
41714178
if buffer_guard.queue_for_processing(event.clone()) {
4172-
// Successfully queued for later processing
4173-
tracing::debug!("Queued post for rate-limited processing");
41744179
} else {
41754180
// Queue full, drop the event
41764181
tracing::warn!(
@@ -4182,9 +4187,6 @@ impl LexiconIngestor for PostIngestor {
41824187
buffer_guard.push(event);
41834188
}
41844189
} else {
4185-
// Can send immediately
4186-
tracing::debug!("📤 Sending post to channel: @{}", post_to_filter.handle);
4187-
41884190
// Always mark activity for watchdog regardless of send success - we're processing messages
41894191
let mut act = self.last_activity_time.lock().await;
41904192
*act = std::time::Instant::now();
@@ -4206,9 +4208,6 @@ impl LexiconIngestor for PostIngestor {
42064208
if let Some(buffer) = &self.buffer {
42074209
let mut buffer_guard = buffer.lock();
42084210
if buffer_guard.queue_for_processing(event.clone()) {
4209-
tracing::debug!(
4210-
"Successfully queued post for rate-limited processing"
4211-
);
42124211
} else {
42134212
tracing::warn!(
42144213
"Backpressure: processing queue full; dropping post from {}",

crates/pattern_core/src/db/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ pub fn strip_dt(s: &str) -> &str {
542542

543543
/// Convert SurrealDB's Datetime type to a chrono DateTime
544544
pub fn from_surreal_datetime(dt: surrealdb::Datetime) -> chrono::DateTime<chrono::Utc> {
545-
let datetime = chrono::NaiveDateTime::parse_from_str(&dt.to_string(), "d'%FT%T%.6fZ'")
545+
let datetime = chrono::NaiveDateTime::parse_from_str(&dt.to_string(), "d'%FT%T%.fZ'")
546546
.expect("should be valid ISO-8601");
547547

548548
datetime.and_utc()

crates/pattern_core/src/message.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1019,7 +1019,9 @@ impl Request {
10191019
for msg in &mut self.messages {
10201020
if msg.role == ChatRole::User || msg.role == ChatRole::System {
10211021
if let MessageContent::Text(text) = &msg.content {
1022-
let timestamp = msg.created_at.naive_local();
1022+
use chrono::TimeZone;
1023+
let time_zone = chrono::Local::now().timezone();
1024+
let timestamp = time_zone.from_utc_datetime(&msg.created_at.naive_utc());
10231025
// injecting created time in to make agents less likely to be confused by artifacts and more temporally aware.
10241026
msg.content = MessageContent::Text(format!(
10251027
"<time_sync>created: {}</time_sync>\n{}",

crates/pattern_core/src/model.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,10 @@ impl ResponseOptions {
124124
"anthropic-beta".to_string(),
125125
"computer-use-2025-01-24".to_string(),
126126
));
127-
headers.push((
128-
"anthropic-beta".to_string(),
129-
"context-1m-2025-08-07".to_string(),
130-
));
127+
// headers.push((
128+
// "anthropic-beta".to_string(),
129+
// "context-1m-2025-08-07".to_string(),
130+
// ));
131131
headers.push((
132132
"anthropic-beta".to_string(),
133133
"code-execution-2025-05-22".to_string(),
@@ -426,7 +426,7 @@ impl GenAiClient {
426426
if let ImageSource::Url(url) = source {
427427
// Quick HEAD request to check if URL is accessible
428428
let client = reqwest::Client::builder()
429-
.timeout(std::time::Duration::from_secs(5))
429+
.timeout(std::time::Duration::from_secs(2))
430430
.build()
431431
.ok();
432432

0 commit comments

Comments
 (0)