Skip to content

Commit 4949b89

Browse files
committed
feat: short prompt for fine-tuned converter models
Fine-tuned models were trained with the short 'Convert recipe to Cooklang:' prompt (recipe-pack finetune crate) but production sent them the full ~1.4k-token instruction prompt - a train/inference mismatch that also wastes tokens on every call. Models with the ft: prefix now get the exact prompt they were trained with; base models keep the full instruction prompt.
1 parent cfd129a commit 4949b89

3 files changed

Lines changed: 39 additions & 3 deletions

File tree

src/converters/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ pub use evaluator::{inject_evaluation, COOKLANG_EVALUATOR_PROMPT};
1212
pub use google::GoogleConverter;
1313
pub use ollama::OllamaConverter;
1414
pub use open_ai::OpenAiConverter;
15-
pub use prompt::{inject_recipe, COOKLANG_CONVERTER_PROMPT};
15+
pub use prompt::{
16+
inject_recipe, prompt_for_model, COOKLANG_CONVERTER_PROMPT, FINETUNED_CONVERTER_PREFIX,
17+
};
1618

1719
use async_trait::async_trait;
1820
use serde::Serialize;

src/converters/open_ai.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::{inject_recipe, ConversionMetadata, ConversionResult, Converter, TokenUsage};
1+
use super::{prompt_for_model, ConversionMetadata, ConversionResult, Converter, TokenUsage};
22
use crate::config::ProviderConfig;
33
use async_trait::async_trait;
44
use log::debug;
@@ -93,7 +93,7 @@ impl Converter for OpenAiConverter {
9393
.json(&json!({
9494
"model": self.model,
9595
"messages": [
96-
{"role": "user", "content": inject_recipe(content)}
96+
{"role": "user", "content": prompt_for_model(&self.model, content)}
9797
],
9898
"temperature": self.temperature,
9999
"max_tokens": self.max_tokens,

src/converters/prompt.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,24 @@ pub fn inject_recipe(recipe_content: &str) -> String {
2828
.replace("{{LANGUAGE}}", &language)
2929
}
3030

31+
/// The user prompt fine-tuned converter models were trained with
32+
/// (see recipe-pack's finetune crate).
33+
pub const FINETUNED_CONVERTER_PREFIX: &str = "Convert recipe to Cooklang:\n\n";
34+
35+
/// Builds the converter prompt for the given model.
36+
///
37+
/// Fine-tuned models (`ft:` prefix) get the short prompt they were trained
38+
/// with — the instruction set is baked into their weights, and sending the
39+
/// full rulebook both mismatches their training distribution and costs
40+
/// ~1.4k extra tokens per call. Base models get the full instruction prompt.
41+
pub fn prompt_for_model(model: &str, recipe_content: &str) -> String {
42+
if model.starts_with("ft:") {
43+
format!("{}{}", FINETUNED_CONVERTER_PREFIX, recipe_content)
44+
} else {
45+
inject_recipe(recipe_content)
46+
}
47+
}
48+
3149
#[cfg(test)]
3250
mod tests {
3351
use super::*;
@@ -44,6 +62,22 @@ mod tests {
4462
assert!(COOKLANG_CONVERTER_PROMPT.contains("timer"));
4563
}
4664

65+
#[test]
66+
fn test_prompt_for_model_finetuned_uses_short_prompt() {
67+
let p = prompt_for_model(
68+
"ft:gpt-4.1-mini-2025-04-14:personal::abc",
69+
"2 eggs\n\nBoil.",
70+
);
71+
assert_eq!(p, "Convert recipe to Cooklang:\n\n2 eggs\n\nBoil.");
72+
}
73+
74+
#[test]
75+
fn test_prompt_for_model_base_uses_full_prompt() {
76+
let p = prompt_for_model("gpt-4.1-mini", "2 eggs\n\nBoil.");
77+
assert!(p.contains("Cooklang syntax rules"));
78+
assert!(p.contains("2 eggs\n\nBoil."));
79+
}
80+
4781
#[test]
4882
fn test_prompt_contains_examples() {
4983
// Verify the prompt includes examples

0 commit comments

Comments
 (0)