Bump version to 1.6.3
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
4ba9706e35
commit
59cfd0e2f5
@@ -0,0 +1,118 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::InferenceTask;
|
||||
use crate::engine::{CancellationToken, GenerationConfig, InferenceEngine};
|
||||
|
||||
pub struct InputClassificationTask;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InputClassificationInput {
|
||||
pub user_input: String,
|
||||
pub recent_commands: Vec<String>,
|
||||
pub is_follow_up: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum InputCategory {
|
||||
Shell,
|
||||
AgentPrompt,
|
||||
}
|
||||
|
||||
pub struct InputClassificationResult {
|
||||
pub category: InputCategory,
|
||||
pub confidence: f32,
|
||||
}
|
||||
|
||||
impl InputClassificationTask {
|
||||
fn build_prompt(input: &InputClassificationInput) -> String {
|
||||
let history = if input.recent_commands.is_empty() {
|
||||
String::from("(none)")
|
||||
} else {
|
||||
input
|
||||
.recent_commands
|
||||
.iter()
|
||||
.take(3)
|
||||
.map(|c| format!("- {c}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
};
|
||||
|
||||
let follow_up_hint = if input.is_follow_up {
|
||||
" The user just received an AI response, so this may be a follow-up."
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
format!(
|
||||
"<|im_start|>system\n\
|
||||
You classify terminal input. Respond with ONLY one word: \"shell\" or \"agent\".\n\
|
||||
\"shell\" = a CLI command the user wants to execute.\n\
|
||||
\"agent\" = a natural language prompt for an AI assistant.{follow_up_hint}\
|
||||
<|im_end|>\n\
|
||||
<|im_start|>user\n\
|
||||
Recent commands:\n{history}\n\
|
||||
Classify this input: \"{}\"\
|
||||
<|im_end|>\n\
|
||||
<|im_start|>assistant\n",
|
||||
input.user_input
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_output(output: &str) -> InputClassificationResult {
|
||||
let output_lower = output.trim().to_lowercase();
|
||||
|
||||
let category = if output_lower.contains("shell") || output_lower.contains("command") {
|
||||
InputCategory::Shell
|
||||
} else {
|
||||
InputCategory::AgentPrompt
|
||||
};
|
||||
|
||||
let confidence = if output_lower == "shell" || output_lower == "agent" {
|
||||
0.95
|
||||
} else {
|
||||
0.7
|
||||
};
|
||||
|
||||
InputClassificationResult {
|
||||
category,
|
||||
confidence,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run_cancellable(
|
||||
&self,
|
||||
engine: &InferenceEngine,
|
||||
input: InputClassificationInput,
|
||||
cancel: &CancellationToken,
|
||||
) -> anyhow::Result<InputClassificationResult> {
|
||||
let prompt = Self::build_prompt(&input);
|
||||
let config = GenerationConfig {
|
||||
max_tokens: 4,
|
||||
temperature: 0.0,
|
||||
top_p: 1.0,
|
||||
};
|
||||
let output = engine.generate_cancellable(&prompt, &config, cancel).await?;
|
||||
Ok(Self::parse_output(&output))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl InferenceTask for InputClassificationTask {
|
||||
type Input = InputClassificationInput;
|
||||
type Output = InputClassificationResult;
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
engine: &InferenceEngine,
|
||||
input: Self::Input,
|
||||
) -> anyhow::Result<InputClassificationResult> {
|
||||
let prompt = Self::build_prompt(&input);
|
||||
let config = GenerationConfig {
|
||||
max_tokens: 4,
|
||||
temperature: 0.0,
|
||||
top_p: 1.0,
|
||||
};
|
||||
let output = engine.generate(&prompt, &config).await?;
|
||||
Ok(Self::parse_output(&output))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
mod input_classification;
|
||||
mod prompt_suggestion;
|
||||
mod tab_naming;
|
||||
|
||||
pub use input_classification::{
|
||||
InputCategory, InputClassificationInput, InputClassificationResult, InputClassificationTask,
|
||||
};
|
||||
pub use prompt_suggestion::{PromptSuggestionInput, PromptSuggestionTask};
|
||||
pub use tab_naming::{TabNamingInput, TabNamingTask};
|
||||
@@ -0,0 +1,64 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::InferenceTask;
|
||||
use crate::engine::{GenerationConfig, InferenceEngine};
|
||||
|
||||
pub struct PromptSuggestionTask;
|
||||
|
||||
pub struct PromptSuggestionInput {
|
||||
pub recent_commands: Vec<String>,
|
||||
pub current_input: String,
|
||||
pub working_directory: String,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl InferenceTask for PromptSuggestionTask {
|
||||
type Input = PromptSuggestionInput;
|
||||
type Output = Vec<String>;
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
engine: &InferenceEngine,
|
||||
input: Self::Input,
|
||||
) -> anyhow::Result<Vec<String>> {
|
||||
let history = input
|
||||
.recent_commands
|
||||
.iter()
|
||||
.take(5)
|
||||
.map(|c| format!("- {c}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
|
||||
let prompt = format!(
|
||||
"<|im_start|>system\n\
|
||||
You suggest terminal commands. Give exactly 3 suggestions, one per line. \
|
||||
No numbering, no explanation, just the commands.\
|
||||
<|im_end|>\n\
|
||||
<|im_start|>user\n\
|
||||
Directory: {}\n\
|
||||
Recent commands:\n{}\n\
|
||||
Current partial input: \"{}\"\n\
|
||||
Suggest 3 likely next commands:\
|
||||
<|im_end|>\n\
|
||||
<|im_start|>assistant\n",
|
||||
input.working_directory, history, input.current_input
|
||||
);
|
||||
|
||||
let config = GenerationConfig {
|
||||
max_tokens: 64,
|
||||
temperature: 0.6,
|
||||
top_p: 0.9,
|
||||
};
|
||||
|
||||
let output = engine.generate(&prompt, &config).await?;
|
||||
|
||||
let suggestions: Vec<String> = output
|
||||
.lines()
|
||||
.map(|l| l.trim().to_string())
|
||||
.filter(|l| !l.is_empty())
|
||||
.take(3)
|
||||
.collect();
|
||||
|
||||
Ok(suggestions)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::InferenceTask;
|
||||
use crate::engine::{GenerationConfig, InferenceEngine};
|
||||
|
||||
pub struct TabNamingTask;
|
||||
|
||||
pub struct TabNamingInput {
|
||||
pub recent_commands: Vec<String>,
|
||||
pub working_directory: String,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl InferenceTask for TabNamingTask {
|
||||
type Input = TabNamingInput;
|
||||
type Output = String;
|
||||
|
||||
async fn run(&self, engine: &InferenceEngine, input: Self::Input) -> anyhow::Result<String> {
|
||||
let commands_str = input
|
||||
.recent_commands
|
||||
.iter()
|
||||
.take(5)
|
||||
.map(|c| format!("- {c}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
|
||||
let prompt = format!(
|
||||
"<|im_start|>system\n\
|
||||
You name terminal tabs. Respond with ONLY a short name (2-4 words max). No explanation.\
|
||||
<|im_end|>\n\
|
||||
<|im_start|>user\n\
|
||||
Directory: {}\n\
|
||||
Recent commands:\n{}\n\
|
||||
What should this tab be named?\
|
||||
<|im_end|>\n\
|
||||
<|im_start|>assistant\n",
|
||||
input.working_directory, commands_str
|
||||
);
|
||||
|
||||
let config = GenerationConfig {
|
||||
max_tokens: 12,
|
||||
temperature: 0.3,
|
||||
top_p: 0.9,
|
||||
};
|
||||
|
||||
let output = engine.generate(&prompt, &config).await?;
|
||||
|
||||
// Clean up: take only the first line, strip quotes
|
||||
let name = output
|
||||
.lines()
|
||||
.next()
|
||||
.unwrap_or(&output)
|
||||
.trim()
|
||||
.trim_matches('"')
|
||||
.trim_matches('\'')
|
||||
.to_string();
|
||||
|
||||
Ok(name)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user