Skip to content

Commit 6857e3c

Browse files
committed
issue fixes
1 parent 772cbdb commit 6857e3c

File tree

12 files changed

+82
-44
lines changed

12 files changed

+82
-44
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ members = [
1818
]
1919

2020
[workspace.package]
21-
version = "0.3.27"
21+
version = "0.3.28"
2222
edition = "2021"
2323
license = "Apache-2.0 OR MIT"
2424
repository = "https://github.com/RightNow-AI/openfang"

crates/openfang-channels/src/bluesky.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,11 @@ impl ChannelAdapter for BlueskyAdapter {
435435
service_url
436436
);
437437
if let Some(ref seen) = last_seen_at {
438-
url.push_str(&format!("&seenAt={}", seen));
438+
let encoded: String = url::form_urlencoded::Serializer::new(String::new())
439+
.append_pair("seenAt", seen)
440+
.finish();
441+
url.push('&');
442+
url.push_str(&encoded);
439443
}
440444

441445
let resp = match client.get(&url).bearer_auth(&token).send().await {

crates/openfang-cli/src/main.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,6 +1163,10 @@ fn cmd_init(quick: bool) {
11631163

11641164
if quick {
11651165
cmd_init_quick(&openfang_dir);
1166+
} else if !std::io::IsTerminal::is_terminal(&std::io::stdout()) {
1167+
ui::hint("Non-interactive terminal detected — running in quick mode");
1168+
ui::hint("For the interactive wizard, run: openfang init (in a terminal)");
1169+
cmd_init_quick(&openfang_dir);
11661170
} else {
11671171
cmd_init_interactive(&openfang_dir);
11681172
}

crates/openfang-cli/src/mcp.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,13 @@ fn read_message(reader: &mut impl BufRead) -> io::Result<Option<Value>> {
216216
/// Write a Content-Length framed JSON-RPC response to the writer.
217217
fn write_message(writer: &mut impl Write, msg: &Value) {
218218
let body = serde_json::to_string(msg).unwrap_or_default();
219-
let _ = write!(writer, "Content-Length: {}\r\n\r\n{}", body.len(), body);
220-
let _ = writer.flush();
219+
if let Err(e) = write!(writer, "Content-Length: {}\r\n\r\n{}", body.len(), body) {
220+
eprintln!("MCP write error: {e}");
221+
return;
222+
}
223+
if let Err(e) = writer.flush() {
224+
eprintln!("MCP flush error: {e}");
225+
}
221226
}
222227

223228
/// Handle a JSON-RPC message and return an optional response.
@@ -234,7 +239,7 @@ fn handle_message(backend: &McpBackend, msg: &Value) -> Option<Value> {
234239
},
235240
"serverInfo": {
236241
"name": "openfang",
237-
"version": "0.1.0"
242+
"version": env!("CARGO_PKG_VERSION")
238243
}
239244
});
240245
Some(jsonrpc_response(id?, result))

crates/openfang-hands/bundled/browser/HAND.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ tools = [
1717
key = "python3"
1818
label = "Python 3 must be installed"
1919
requirement_type = "binary"
20-
check_value = "python"
20+
check_value = "python3"
2121
description = "Python 3 is required to run Playwright, the browser automation library that powers this hand."
2222

2323
[requires.install]

crates/openfang-hands/src/registry.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,15 @@ impl Default for HandRegistry {
311311
fn check_requirement(req: &HandRequirement) -> bool {
312312
match req.requirement_type {
313313
RequirementType::Binary => {
314-
// Check if binary exists on PATH
315-
which_binary(&req.check_value)
314+
// Check if binary exists on PATH.
315+
// For python3, also try "python" (Windows ships python not python3).
316+
if which_binary(&req.check_value) {
317+
return true;
318+
}
319+
if req.check_value == "python3" {
320+
return which_binary("python");
321+
}
322+
false
316323
}
317324
RequirementType::EnvVar | RequirementType::ApiKey => {
318325
// Check if env var is set and non-empty

crates/openfang-kernel/src/kernel.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,8 @@ impl OpenFangKernel {
791791
if let Some(ref provider) = config.memory.embedding_provider {
792792
// Explicit config takes priority — use the configured embedding model
793793
let api_key_env = config.memory.embedding_api_key_env.as_deref().unwrap_or("");
794-
match create_embedding_driver(provider, configured_model, api_key_env) {
794+
let custom_url = config.provider_urls.get(provider.as_str()).map(|s| s.as_str());
795+
match create_embedding_driver(provider, configured_model, api_key_env, custom_url) {
795796
Ok(d) => {
796797
info!(provider = %provider, model = %configured_model, "Embedding driver configured from memory config");
797798
Some(Arc::from(d))
@@ -807,7 +808,7 @@ impl OpenFangKernel {
807808
} else {
808809
configured_model.as_str()
809810
};
810-
match create_embedding_driver("openai", model, "OPENAI_API_KEY") {
811+
match create_embedding_driver("openai", model, "OPENAI_API_KEY", None) {
811812
Ok(d) => {
812813
info!("Embedding driver auto-detected: OpenAI");
813814
Some(Arc::from(d))
@@ -824,7 +825,8 @@ impl OpenFangKernel {
824825
} else {
825826
configured_model.as_str()
826827
};
827-
match create_embedding_driver("ollama", model, "") {
828+
let ollama_url = config.provider_urls.get("ollama").map(|s| s.as_str());
829+
match create_embedding_driver("ollama", model, "", ollama_url) {
828830
Ok(d) => {
829831
info!("Embedding driver auto-detected: Ollama (local)");
830832
Some(Arc::from(d))
@@ -2342,6 +2344,9 @@ impl OpenFangKernel {
23422344
.update_session_id(agent_id, new_session.id)
23432345
.map_err(KernelError::OpenFang)?;
23442346

2347+
// Reset quota tracking so /new clears "token quota exceeded"
2348+
self.scheduler.reset_usage(agent_id);
2349+
23452350
info!(agent_id = %agent_id, "Session reset (summary saved to memory)");
23462351
Ok(())
23472352
}

crates/openfang-kernel/src/scheduler.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,15 @@ impl AgentScheduler {
100100
Ok(())
101101
}
102102

103+
/// Reset usage tracking for an agent (e.g. on session reset).
104+
pub fn reset_usage(&self, agent_id: AgentId) {
105+
if let Some(mut tracker) = self.usage.get_mut(&agent_id) {
106+
tracker.total_tokens = 0;
107+
tracker.tool_calls = 0;
108+
tracker.window_start = Instant::now();
109+
}
110+
}
111+
103112
/// Abort an agent's active task.
104113
pub fn abort_task(&self, agent_id: AgentId) {
105114
if let Some((_, handle)) = self.tasks.remove(&agent_id) {

crates/openfang-runtime/src/embedding.rs

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -179,27 +179,31 @@ pub fn create_embedding_driver(
179179
provider: &str,
180180
model: &str,
181181
api_key_env: &str,
182+
custom_base_url: Option<&str>,
182183
) -> Result<Box<dyn EmbeddingDriver + Send + Sync>, EmbeddingError> {
183184
let api_key = if api_key_env.is_empty() {
184185
String::new()
185186
} else {
186187
std::env::var(api_key_env).unwrap_or_default()
187188
};
188189

189-
let base_url = match provider {
190-
"openai" => OPENAI_BASE_URL.to_string(),
191-
"groq" => GROQ_BASE_URL.to_string(),
192-
"together" => TOGETHER_BASE_URL.to_string(),
193-
"fireworks" => FIREWORKS_BASE_URL.to_string(),
194-
"mistral" => MISTRAL_BASE_URL.to_string(),
195-
"ollama" => OLLAMA_BASE_URL.to_string(),
196-
"vllm" => VLLM_BASE_URL.to_string(),
197-
"lmstudio" => LMSTUDIO_BASE_URL.to_string(),
198-
other => {
199-
warn!("Unknown embedding provider '{other}', using OpenAI-compatible format");
200-
format!("https://{other}/v1")
201-
}
202-
};
190+
let base_url = custom_base_url
191+
.filter(|u| !u.is_empty())
192+
.map(|u| u.to_string())
193+
.unwrap_or_else(|| match provider {
194+
"openai" => OPENAI_BASE_URL.to_string(),
195+
"groq" => GROQ_BASE_URL.to_string(),
196+
"together" => TOGETHER_BASE_URL.to_string(),
197+
"fireworks" => FIREWORKS_BASE_URL.to_string(),
198+
"mistral" => MISTRAL_BASE_URL.to_string(),
199+
"ollama" => OLLAMA_BASE_URL.to_string(),
200+
"vllm" => VLLM_BASE_URL.to_string(),
201+
"lmstudio" => LMSTUDIO_BASE_URL.to_string(),
202+
other => {
203+
warn!("Unknown embedding provider '{other}', using OpenAI-compatible format");
204+
format!("https://{other}/v1")
205+
}
206+
});
203207

204208
// SECURITY: Warn when embedding requests will be sent to an external API
205209
let is_local = base_url.contains("localhost")
@@ -351,7 +355,7 @@ mod tests {
351355
#[test]
352356
fn test_create_embedding_driver_ollama() {
353357
// Should succeed even without API key (ollama is local)
354-
let driver = create_embedding_driver("ollama", "all-MiniLM-L6-v2", "");
358+
let driver = create_embedding_driver("ollama", "all-MiniLM-L6-v2", "", None);
355359
assert!(driver.is_ok());
356360
assert_eq!(driver.unwrap().dimensions(), 384);
357361
}

0 commit comments

Comments
 (0)