Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
502ceb0
fix(inference): recover managed charged-USD in the crate-native invok…
senamakel Jul 11, 2026
a3c33b6
feat(inference): add create_turn_chat_model — per-(role,model) crate-…
senamakel Jul 11, 2026
6b234f3
feat(inference): crate-native build_turn_models path + TurnModelSourc…
senamakel Jul 11, 2026
954dd12
feat(inference): flip sub-agent extract summarizer to crate-native (P…
senamakel Jul 11, 2026
825d207
feat(inference): flip triage (subconscious) turn to crate-native, pre…
senamakel Jul 11, 2026
0056916
chore(vendor): bump tinyagents 4fc8cd8 -> 40cd29b (v1.9.0 + #55 promp…
senamakel Jul 11, 2026
37d2168
test(triage): add crate_native to ResolvedProvider ctors in raw_cover…
senamakel Jul 11, 2026
8e091e0
fix(inference): revert sub-agent extract flip — keep resolved provide…
senamakel Jul 11, 2026
2528fa0
feat(inference): flip the main-turn session builder to crate-native (…
senamakel Jul 11, 2026
c31bde7
revert(inference): un-flip main-turn session builder — broke agent_ha…
senamakel Jul 11, 2026
2bc0a45
feat(inference): re-flip session builder crate-native + SSE-capable e…
senamakel Jul 11, 2026
e6287f7
feat(inference): flip channel turn to crate-native (P3-B, producer 4)
senamakel Jul 11, 2026
fbfc3e7
chore(vendor): bump tinyagents -> 014b345a (stream() JSON fallback)
senamakel Jul 11, 2026
600848f
feat(inference): flip sub-agent runner turns to crate-native (P3-B, p…
senamakel Jul 11, 2026
44b1e29
fix(inference): preserve resolved subagent provider
senamakel Jul 11, 2026
eb43642
refactor(inference): port one-shot consumers to chat models
senamakel Jul 12, 2026
c085273
refactor(inference): build explicit providers as chat models
senamakel Jul 12, 2026
105a1fc
refactor(memory-tree): use crate-native local summarizer
senamakel Jul 12, 2026
0e21a53
refactor(agent): drop provider retention from native turns
senamakel Jul 12, 2026
fe4330c
refactor(agent): resolve subagents through model sources
senamakel Jul 12, 2026
cfe1788
refactor(channels): stop caching production providers
senamakel Jul 12, 2026
70c3091
refactor(triage): carry crate-native model sources
senamakel Jul 12, 2026
faab86d
refactor(inference): port remaining one-shot provider calls
senamakel Jul 12, 2026
2dc28ab
test(memory-tree): remove stale provider test
senamakel Jul 12, 2026
edd79b8
chore(tauri): refresh tinyagents lock entry
senamakel Jul 12, 2026
e761dc0
refactor(agent): keep integrations text mode crate-native
senamakel Jul 12, 2026
6ff9dbd
refactor(inference): remove legacy provider adapters from live calls
senamakel Jul 12, 2026
7291301
refactor(inference): delete host compatible wire client
senamakel Jul 12, 2026
3d7618d
fix(inference): preserve crate adapter parity edges
senamakel Jul 12, 2026
d63ab42
fix(inference): emit one tool start per streamed call
senamakel Jul 12, 2026
44b2c9e
fix(inference): bridge legacy streaming to crate model
senamakel Jul 12, 2026
4e47f28
test(inference): serve native SSE content type
senamakel Jul 12, 2026
2fb2026
test(memory): disambiguate host tool memory store
senamakel Jul 12, 2026
a798cb0
Merge remote-tracking branch 'upstream/main' into feat/inference-phas…
senamakel Jul 12, 2026
50827e4
test(memory): construct crate store through host bridge
senamakel Jul 12, 2026
ba6c99a
fix(agent): preserve injected text-mode providers
senamakel Jul 12, 2026
7162a22
Merge remote-tracking branch 'upstream/main' into feat/inference-phas…
senamakel Jul 12, 2026
25b5079
test(inference): preserve streamed content whitespace
senamakel Jul 12, 2026
2c70040
test(inference): preserve streamed reasoning whitespace
senamakel Jul 12, 2026
bd801f8
test(inference): expect crate-native stream usage
senamakel Jul 12, 2026
96e039c
test(inference): retain embedded tool json as text
senamakel Jul 12, 2026
970913a
test(inference): preserve compatible response content
senamakel Jul 12, 2026
33ddcc1
test(inference): expect generic reasoning projection
senamakel Jul 12, 2026
0088241
test(inference): drop legacy function usage projection
senamakel Jul 12, 2026
35ec213
test(inference): assert crate normalized 404
senamakel Jul 12, 2026
9cea5b4
test(inference): retain crate-native tool specs
senamakel Jul 12, 2026
d0e1e2f
test(inference): drop legacy tool synthesis expectations
senamakel Jul 12, 2026
805698f
test(inference): retain standard compatible usage
senamakel Jul 12, 2026
96dd012
test(inference): assert attributable tool delta
senamakel Jul 12, 2026
c8bffe3
test(inference): preserve crate request transcript
senamakel Jul 12, 2026
f532231
test(inference): omit generic reasoning extension
senamakel Jul 12, 2026
c538d8c
test(inference): accept crate missing-key wording
senamakel Jul 12, 2026
9cccdc0
test(inference): expect unauthenticated dispatch error
senamakel Jul 12, 2026
11315b2
test(inference): assert streamed provider failure
senamakel Jul 12, 2026
02b651e
test(inference): expect history stream fallback chunk
senamakel Jul 12, 2026
79a0665
test(inference): configure crate provider base URL
senamakel Jul 12, 2026
c6ad431
chore(tinyagents): update to latest main
senamakel Jul 12, 2026
fcd1f12
test(tinyagents): initialize tool validity metadata
senamakel Jul 12, 2026
526bceb
test(inference): expect latest reasoning extraction
senamakel Jul 12, 2026
47ad1ec
test(inference): match legacy reasoning projection
senamakel Jul 12, 2026
b05e49a
test(inference): retain legacy usage boundary
senamakel Jul 12, 2026
85bed57
test(inference): assert sanitized stream denial
senamakel Jul 12, 2026
8930578
fix(inference): redact crate provider errors
senamakel Jul 12, 2026
3c167f5
fix(inference): preserve context while redacting tokens
senamakel Jul 12, 2026
3943787
test(inference): align compatibility matrix with crate errors
senamakel Jul 12, 2026
1ad0562
test(inference): expect standard compatible usage
senamakel Jul 12, 2026
777cdec
test(inference): cover latest fallback leniency
senamakel Jul 12, 2026
9c4300d
test(inference): remove speculative compatibility retries
senamakel Jul 12, 2026
d1f9070
test(inference): retain duplicate tool schemas
senamakel Jul 12, 2026
5f79b47
test(inference): align remaining crate compatibility suites
senamakel Jul 13, 2026
c4a8543
test(inference): accept empty system-only response
senamakel Jul 13, 2026
95df7ae
test(inference): cover compatible endpoint leniency
senamakel Jul 13, 2026
92b5bd4
test(inference): assert empty bearer wire header
senamakel Jul 13, 2026
bdad55c
test(inference): assert provider key isolation
senamakel Jul 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion app/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

76 changes: 37 additions & 39 deletions src/bin/inference_probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ use anyhow::{Context, Result};
use clap::Parser;
use openhuman_core::openhuman::agent::Agent;
use openhuman_core::openhuman::config::Config;
use openhuman_core::openhuman::inference::provider::create_chat_provider;
use openhuman_core::openhuman::inference::provider::traits::{ChatMessage, ChatRequest};
use openhuman_core::openhuman::tools::traits::ToolSpec;
use openhuman_core::openhuman::inference::provider::create_chat_model_with_model_id;
use serde_json::json;
use tinyagents::harness::message::Message;
use tinyagents::harness::model::ModelRequest;
use tinyagents::harness::tool::ToolSchema;

#[derive(Parser, Debug)]
#[command(name = "inference-probe")]
Expand Down Expand Up @@ -117,14 +118,16 @@ async fn run_harness(config: &Config, prompt: &str) -> Result<()> {
}

async fn run_raw(config: &Config, role: &str, raw_mode: &str, prompt: &str) -> Result<()> {
let (provider, model_name) =
create_chat_provider(role, config).context("create_chat_provider failed")?;
let (chat_model, model_name) = create_chat_model_with_model_id(role, config, 0.4)
.context("create_chat_model_with_model_id failed")?;
eprintln!("[probe] mode = raw");
eprintln!("[probe] role = {role}");
eprintln!("[probe] resolved model = {model_name}");
eprintln!(
"[probe] provider.supports_native_tools() = {}",
provider.supports_native_tools()
"[probe] model.profile.tool_calling = {}",
chat_model
.profile()
.is_some_and(|profile| profile.tool_calling)
);
eprintln!("[probe] raw_mode = {raw_mode}");

Expand All @@ -149,82 +152,77 @@ Emit tool calls as `<tool_call>name[arg1|arg2]</tool_call>` blocks.
"You are a helpful assistant. Use the provided tools when the user asks something \
a tool can answer.";

let (system, tools_for_request): (&str, Option<Vec<ToolSpec>>) = match raw_mode {
let (system, tools_for_request): (&str, Option<Vec<ToolSchema>>) = match raw_mode {
"native" => (
native_system,
Some(vec![
ToolSpec {
name: "get_weather".into(),
description: "Get the current weather for a city.".into(),
parameters: json!({
ToolSchema::new(
"get_weather",
"Get the current weather for a city.",
json!({
"type": "object",
"properties": {
"city": {"type": "string"},
"unit": {"type": "string", "enum": ["metric", "imperial"]}
},
"required": ["city"]
}),
},
ToolSpec {
name: "list_files".into(),
description: "List files in a directory.".into(),
parameters: json!({
),
ToolSchema::new(
"list_files",
"List files in a directory.",
json!({
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"]
}),
},
),
]),
),
"pformat" => (pformat_system, None),
other => anyhow::bail!("unknown --raw-mode {other:?}"),
};

let messages = vec![
ChatMessage::system(system),
ChatMessage::user(prompt.to_string()),
];

let request = ChatRequest {
messages: &messages,
tools: tools_for_request.as_deref(),
stream: None,
max_tokens: None,
};
let mut request = ModelRequest::new(vec![Message::system(system), Message::user(prompt)])
.with_model(model_name.clone())
.with_temperature(0.4);
if let Some(tools) = tools_for_request {
request = request.with_tools(tools);
}

eprintln!("[probe] >>> raw provider.chat()...");
eprintln!("[probe] >>> raw ChatModel::invoke()...");
let started = std::time::Instant::now();
let response = provider
.chat(request, &model_name, 0.4)
let response = chat_model
.invoke(&(), request)
.await
.context("provider.chat() failed")?;
.context("ChatModel::invoke() failed")?;
let elapsed = started.elapsed();
eprintln!("[probe] <<< {elapsed:?}");

println!();
println!("=== RAW RESPONSE TEXT ===");
println!("{}", response.text_or_empty());
println!("{}", response.text());
println!("=== /RAW RESPONSE TEXT ===");

let text = response.text_or_empty();
let text = response.text();
let saw_pformat = text.contains("<tool_call>") || text.contains("<toolcall>");
let saw_native = !response.tool_calls.is_empty();
let saw_native = !response.tool_calls().is_empty();
eprintln!(
"[probe] response.tool_calls.len() = {}",
response.tool_calls.len()
response.tool_calls().len()
);
eprintln!(
"[probe] response contains <tool_call> tag = {}",
saw_pformat
);
if saw_native {
for tc in &response.tool_calls {
for tc in response.tool_calls() {
// Print tool name + arg byte-count only — raw arguments may
// contain user content / secrets and must not land in logs.
eprintln!(
"[probe] native tool_call name={} arg_bytes={}",
tc.name,
tc.arguments.len()
tc.arguments.to_string().len()
);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/openhuman/agent/harness/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pub(crate) async fn run_channel_turn_via_graph(
// Phase 3 / Motion A): the harness graph names crate model types only, and
// reads native-tool / vision capability + telemetry id off the built bundle.
let context_window = source.effective_context_window(model).await;
let turn_models = source.build(model, temperature, context_window);
let turn_models = source.build(model, temperature, context_window)?;

// Native-tool support drives the durable history-suffix dispatcher (native
// envelope vs prompt-guided text) at the end of this turn; capture it before
Expand Down
46 changes: 23 additions & 23 deletions src/openhuman/agent/harness/session/builder/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::openhuman::agent::host_runtime;
use crate::openhuman::agent_memory::memory_loader::DefaultMemoryLoader;
use crate::openhuman::config::Config;
use crate::openhuman::context::prompt::SystemPromptBuilder;
use crate::openhuman::inference::provider::{self, Provider};
use crate::openhuman::inference::provider;
use crate::openhuman::memory::Memory;
use crate::openhuman::memory_store;
use crate::openhuman::memory_tools::ToolMemoryCaptureHook;
Expand Down Expand Up @@ -476,14 +476,21 @@ impl Agent {
// double-retry every transient error. Cross-route fallback is likewise
// the crate registry `FallbackPolicy`, so `config.reliability.*` no longer
// layers on the turn path (it still governs the non-seam provider paths).
let (provider, mut model_name): (Box<dyn Provider>, String) =
crate::openhuman::inference::provider::create_chat_provider(provider_role, config)?;
let (resolved_chat_model, mut model_name) =
crate::openhuman::inference::provider::create_chat_model_with_model_id(
provider_role,
config,
config.default_temperature,
)?;
let supports_native = resolved_chat_model
.profile()
.map_or(true, |profile| profile.tool_calling);
log::info!(
"[session-builder] agent_id={} provider_role={} resolved_model={} supports_native_tools={}",
agent_id,
provider_role,
model_name,
provider.supports_native_tools()
supports_native
);
let target_agent_id = target_def
.map(|def| def.id.as_str())
Expand Down Expand Up @@ -530,7 +537,6 @@ impl Agent {
// the choice string now so the provider borrow doesn't conflict
// with the later `provider` move into the builder.
let dispatcher_choice = config.agent.tool_dispatcher.clone();
let supports_native = provider.supports_native_tools();

// Build prompt builder — either the default "orchestrator /
// main agent" layout that bootstraps from workspace identity
Expand Down Expand Up @@ -693,23 +699,12 @@ impl Agent {
> = if config.learning.reflection_source
== crate::openhuman::config::ReflectionSource::Cloud
{
// Reflection always calls with the `hint:reasoning` route +
// 0.3 temperature (formerly `simple_chat(prompt,
// "hint:reasoning", 0.3)`), so bake both into the wrapped
// model. The routed provider still resolves the hint per call.
let routed = provider::create_routed_provider(
config.inference_url.as_deref(),
config.api_url.as_deref(),
config.api_key.as_deref(),
&config.reliability,
&config.model_routes,
&model_name,
)?;
Some(provider::chat_model_from_provider(
routed,
"hint:reasoning".to_string(),
0.3,
))
let (model, resolved_model) =
provider::create_chat_model_with_model_id("reasoning", config, 0.3)?;
log::debug!(
"[learning] built crate-native reflection model resolved_model={resolved_model}"
);
Some(model)
} else {
None
};
Expand Down Expand Up @@ -1140,8 +1135,13 @@ impl Agent {
None
};

// Crate-native turn models (Phase 3 P3-B): the production main-turn agent
// builds crate `ChatModel`s from `(provider_role, config)` without retaining
// a host provider. The
// `agent_harness_e2e` mock now serves SSE for streaming, so the crate-native
// streaming path is exercised end-to-end.
let mut builder = Agent::builder()
.provider(provider)
.crate_native_provider(provider_role, std::sync::Arc::new(config.clone()))
.tools(tools)
.visible_tool_names(visible)
.memory(memory)
Expand Down
17 changes: 17 additions & 0 deletions src/openhuman/agent/harness/session/builder/setters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,23 @@ impl AgentBuilder {
self
}

/// Sets the AI provider as a **crate-native** turn-model source (Phase 3 P3-B):
/// `build`/`build_summarizer` construct crate `ChatModel`s from `(role, config)`
/// via `create_turn_chat_model` (managed → `OpenHumanBackendModel`, local/cloud →
/// crate `OpenAiModel`) instead of wrapping `provider` in `ProviderModel`s.
/// Used by the production session factory; the plain
/// [`provider`](Self::provider) setter (Provider path) stays for tests that
/// inject a mock they observe.
pub fn crate_native_provider(
mut self,
role: impl Into<String>,
config: Arc<crate::openhuman::config::Config>,
) -> Self {
self.turn_model_source =
Some(crate::openhuman::tinyagents::TurnModelSource::new_crate_native(role, config));
self
}

/// Sets the available tools for the agent.
pub fn tools(mut self, tools: Vec<Box<dyn Tool>>) -> Self {
self.tools = Some(tools);
Expand Down
2 changes: 1 addition & 1 deletion src/openhuman/agent/harness/session/turn/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -879,7 +879,7 @@ impl Agent {
.await;
let turn_models =
self.turn_model_source
.build(effective_model, temperature, context_window);
.build(effective_model, temperature, context_window)?;

// Honor custom/BYOK vision models too: they can set `model_vision` even
// when the provider capability bit is false, and must still rehydrate
Expand Down
Loading
Loading