-
Notifications
You must be signed in to change notification settings - Fork 25
fix(server): disable GGML_CUDA_GRAPHS to avoid ggml CUDA graph key collision crash #460
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
109f9ad
da32422
621a0f3
2092fb1
f712de7
03d6683
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,23 @@ | ||
| //! Demonstrates Graph-of-Thought (GoT) for hierarchical aggregation. | ||
| //! | ||
| //! This example generates multiple initial proposals concurrently, then | ||
| //! progressively aggregates them in pairs across multiple levels. The streaming | ||
| //! nature allows aggregation to begin as soon as pairs of proposals are ready, | ||
| //! maximizing parallelism. | ||
| //! progressively aggregates them in pairs across multiple levels. | ||
| //! | ||
| //! # Batched aggregation | ||
| //! | ||
| //! The original implementation processed proposals one at a time in a | ||
| //! `while let Some` loop, which meant aggregation pairs were launched | ||
| //! sequentially. This prevented the runtime scheduler from seeing all | ||
| //! aggregation requests simultaneously and coalescing them into a single | ||
| //! batched forward pass. | ||
| //! | ||
| //! This version collects all proposals first via `future::join_all`, then | ||
| //! launches all aggregation pairs in one shot. The scheduler now receives | ||
| //! all N/2 aggregation `flush()` calls concurrently and can batch them | ||
| //! together, reducing the number of GPU kernel invocations per level from | ||
| //! N/2 sequential firings to ideally 1 batched firing. | ||
|
|
||
| use futures::stream::FuturesUnordered; | ||
| use futures::{StreamExt, future}; | ||
| use futures::future; | ||
| use inferlet::{ | ||
| Context, sample::Sampler, model::Model, | ||
| runtime, Result, | ||
|
|
@@ -31,28 +42,69 @@ fn default_aggregation_tokens() -> usize { 256 } | |
| const SYSTEM_PROMPT: &str = "You are a helpful, respectful and honest assistant."; | ||
|
|
||
| const PROPOSAL_PROMPT_TEMPLATE: &str = "\ | ||
| Could you suggest a method or approach to solve the following question? \ | ||
| Please provide a high-level plan without doing the actual calculation. \ | ||
| Keep it concise, around 80 words. Question: {}"; | ||
|
|
||
| const AGGREGATE_PROMPT: &str = "\ | ||
| Please compare the following solution with the one you just provided \ | ||
| and aggregate their ideas into a single, improved solution:\n"; | ||
| Solve the following math problem step by step. Show your work and give the final numeric answer. \ | ||
| End your response with: ANSWER: <number>\n\ | ||
| Question: {}"; | ||
|
|
||
| const AGGREGATE_PROMPT: &str = ""; // unused - see format_aggregate_prompt below | ||
|
|
||
| /// Pair up a flat list of (text, context) results into aggregation tasks, | ||
| /// launching all pairs simultaneously so the scheduler can batch them. | ||
| /// | ||
| /// If the input has an odd number of items, the last unpaired item is | ||
| /// dropped (its context is released immediately, freeing KV pages). | ||
| fn launch_aggregation_pairs( | ||
| items: Vec<(String, Context)>, | ||
| aggregation_tokens: usize, | ||
| question: String, | ||
| model: &Model, | ||
| ) -> Result<Vec<impl std::future::Future<Output = Result<(String, Context)>> + 'static>> { | ||
| let mut tasks = Vec::new(); | ||
| let mut iter = items.into_iter(); | ||
|
|
||
| while let Some((text_a, _ctx_a)) = iter.next() { | ||
| if let Some((text_b, _ctx_b)) = iter.next() { | ||
| // Both proposal contexts are dropped here — all their KV pages | ||
| // are freed immediately. The aggregation uses a fresh context | ||
| // with only the question + both proposals, so the model always | ||
| // knows what problem it is solving. | ||
| let mut ctx = Context::new(model)?; | ||
| let prompt = format!( | ||
| "You are solving this math problem: {}\n\nHere are two proposed solutions:\n\nSolution A:\n{}\n\nSolution B:\n{}\n\nPick the correct answer. Show your reasoning briefly. End with: ANSWER: <number>", | ||
| question.clone(), text_a, text_b | ||
| ); | ||
| ctx.system(SYSTEM_PROMPT); | ||
| ctx.user(&prompt); | ||
| ctx.cue(); | ||
| tasks.push(async move { | ||
| let text = ctx | ||
| .generate(Sampler::TopP { temperature: 0.6, p: 0.95 }) | ||
| .max_tokens(aggregation_tokens) | ||
| .collect_text() | ||
| .await?; | ||
| Ok((text, ctx)) | ||
| }); | ||
| } | ||
| // Odd item out: context dropped, KV pages freed. | ||
| } | ||
|
Comment on lines
+65
to
+89
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Confirm the default proposal count to gauge how often N<4 is hit.
rg -nP 'fn default_proposal_tokens' -A6 inferlets/graph-of-thought/src/lib.rsRepository: pie-project/pie Length of output: 541 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the aggregation logic and its caller.
nl -ba inferlets/graph-of-thought/src/lib.rs | sed -n '1,220p'
echo
echo "---- call sites ----"
rg -n "run_hierarchical_aggregation|final_results|default_proposal_tokens" inferlets/graph-of-thought/src/lib.rsRepository: pie-project/pie Length of output: 193 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Read the relevant slice with line numbers using available tools.
sed -n '1,220p' inferlets/graph-of-thought/src/lib.rs | cat -n
echo
echo "---- call sites ----"
rg -n "run_hierarchical_aggregation|final_results|default_proposal_tokens" inferlets/graph-of-thought/src/lib.rsRepository: pie-project/pie Length of output: 9088 Carry the unpaired item into the next aggregation level 🤖 Prompt for AI Agents |
||
|
|
||
| Ok(tasks) | ||
| } | ||
|
|
||
| /// Main logic for running the hierarchical aggregation workflow. | ||
| async fn run_hierarchical_aggregation( | ||
| base_context: &mut Context, | ||
| question: &str, | ||
| proposal_tokens: Vec<usize>, | ||
| aggregation_tokens: usize, | ||
| model: &Model, | ||
| ) -> Result<Vec<String>> { | ||
| // --- Stage 1: Generate Initial Proposals --- | ||
| // --- Stage 1: Generate Initial Proposals (all concurrent) --- | ||
| let propose_prompt = PROPOSAL_PROMPT_TEMPLATE.replace("{}", question); | ||
| base_context.user(&propose_prompt); | ||
| base_context.flush().await?; | ||
|
|
||
| let mut proposal_tasks = proposal_tokens | ||
| let proposal_futures = proposal_tokens | ||
| .into_iter() | ||
| .map(|max_tokens| { | ||
| let mut ctx = base_context.fork()?; | ||
|
|
@@ -66,62 +118,35 @@ async fn run_hierarchical_aggregation( | |
| Ok::<_, String>((proposal_text, ctx)) | ||
| }) | ||
| }) | ||
| .collect::<Result<Vec<_>>>()? | ||
| .collect::<Result<Vec<_>>>()?; | ||
|
|
||
| // Collect ALL proposals before pairing. This lets Stage 2 launch all | ||
| // aggregation pairs simultaneously rather than as each proposal dribbles in. | ||
| let proposals: Vec<(String, Context)> = future::join_all(proposal_futures) | ||
| .await | ||
| .into_iter() | ||
| .collect::<FuturesUnordered<_>>(); | ||
|
|
||
| // --- Stage 2: First-Level Aggregation (Pairing Proposals) --- | ||
| let mut first_aggregation_tasks = FuturesUnordered::new(); | ||
| let mut pending_proposal: Option<(String, Context)> = None; | ||
|
|
||
| while let Some(result) = proposal_tasks.next().await { | ||
| let (proposal_text, mut proposal_ctx) = result?; | ||
| if pending_proposal.is_none() { | ||
| pending_proposal = Some((proposal_text, proposal_ctx)); | ||
| } else { | ||
| let (previous_proposal_text, _) = pending_proposal.take().unwrap(); | ||
| let aggregation_prompt = format!("{}{}", AGGREGATE_PROMPT, previous_proposal_text); | ||
| proposal_ctx.user(&aggregation_prompt); | ||
| proposal_ctx.cue(); | ||
|
|
||
| first_aggregation_tasks.push(async move { | ||
| let aggregation_text = proposal_ctx | ||
| .generate(Sampler::TopP { temperature: 0.6, p: 0.95 }) | ||
| .max_tokens(aggregation_tokens) | ||
| .collect_text() | ||
| .await?; | ||
| Ok::<_, String>((aggregation_text, proposal_ctx)) | ||
| }); | ||
| } | ||
| } | ||
| .collect::<Result<_>>()?; | ||
|
|
||
| // --- Stage 2: First-Level Aggregation (all pairs launched at once) --- | ||
| // All N/2 aggregation flush() calls hit the scheduler simultaneously, | ||
| // which can coalesce them into a single batched forward pass. | ||
| let first_agg_futures = launch_aggregation_pairs(proposals, aggregation_tokens, question.to_string(), &model)?; | ||
| let first_aggregations: Vec<(String, Context)> = future::join_all(first_agg_futures) | ||
| .await | ||
| .into_iter() | ||
| .collect::<Result<_>>()?; | ||
|
|
||
| // --- Stage 3: Second-Level Aggregation (Pairing Aggregations) --- | ||
| let mut second_aggregation_tasks = Vec::new(); | ||
| let mut pending_aggregation: Option<(String, Context)> = None; | ||
|
|
||
| while let Some(result) = first_aggregation_tasks.next().await { | ||
| let (aggregation_text, mut aggregation_ctx) = result?; | ||
| if pending_aggregation.is_none() { | ||
| pending_aggregation = Some((aggregation_text, aggregation_ctx)); | ||
| } else { | ||
| let (previous_aggregation_text, _) = pending_aggregation.take().unwrap(); | ||
| let final_prompt = format!("{}{}", AGGREGATE_PROMPT, previous_aggregation_text); | ||
| aggregation_ctx.user(&final_prompt); | ||
| aggregation_ctx.cue(); | ||
|
|
||
| second_aggregation_tasks.push(async move { | ||
| aggregation_ctx | ||
| .generate(Sampler::TopP { temperature: 0.6, p: 0.95 }) | ||
| .max_tokens(aggregation_tokens) | ||
| .collect_text() | ||
| .await | ||
| }); | ||
| } | ||
| } | ||
| // --- Stage 3: Second-Level Aggregation (all pairs launched at once) --- | ||
| let second_agg_futures = launch_aggregation_pairs(first_aggregations, aggregation_tokens, question.to_string(), &model)?; | ||
| let final_results: Vec<String> = future::join_all(second_agg_futures) | ||
| .await | ||
| .into_iter() | ||
| .collect::<Result<Vec<(String, Context)>>>()? | ||
| .into_iter() | ||
| .map(|(text, _ctx)| text) | ||
| .collect(); | ||
|
|
||
| // --- Stage 4: Collect Final Results --- | ||
| let results = future::join_all(second_aggregation_tasks).await; | ||
| results.into_iter().collect::<Result<Vec<_>>>() | ||
| Ok(final_results) | ||
| } | ||
|
|
||
| #[inferlet::main] | ||
|
|
@@ -153,6 +178,7 @@ async fn main(input: Input) -> Result<String> { | |
| &question, | ||
| proposal_tokens, | ||
| aggregation_tokens, | ||
| &model, | ||
| ) | ||
| .await?; | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: pie-project/pie
Length of output: 229
🏁 Script executed:
Repository: pie-project/pie
Length of output: 4843
Add the missing
src/ops/flashinfer_moe_stub.cppfiledriver/cuda/CMakeLists.txtwires insrc/ops/flashinfer_moe_stub.cppwhenPIE_CUDA_BUILD_MOE=OFF, but that file is not present. Default OFF builds will fail as soon as CMake tries to compile it. Either add the stub with the threeops::flashinfer_cutlass_moe_*symbols or stop referencing it here.🤖 Prompt for AI Agents