pub mod anthropic;
pub mod openai;
use crate::acg::plugin::PluginInput;
use crate::acg::prompt_ir::SpanId;
use crate::acg::types::{SharingScope, TranslationReport};
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct HintTranslation {
pub hint_plan: HintPlan,
pub translation_report: TranslationReport,
}
pub(crate) trait HintTranslator: Send + Sync {
fn provider_id(&self) -> &str;
fn translate(&self, input: &PluginInput<'_>) -> crate::acg::Result<HintTranslation>;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct HintPlan {
pub provider: String,
pub directives: Vec<HintDirective>,
}
impl HintPlan {
pub fn new(provider: impl Into<String>) -> Self {
Self {
provider: provider.into(),
directives: Vec::new(),
}
}
pub fn push(&mut self, directive: impl Into<HintDirective>) {
self.directives.push(directive.into());
}
pub fn has_anthropic_breakpoint(&self) -> bool {
self.directives.iter().any(|directive| {
matches!(
directive,
HintDirective::Anthropic(AnthropicHintDirective::CacheBreakpoint { .. })
)
})
}
}
#[cfg_attr(not(test), allow(dead_code))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum HintTarget {
Span {
span_id: SpanId,
},
StablePrefix {
end_exclusive: usize,
last_span_id: Option<SpanId>,
},
}
impl HintTarget {
#[cfg_attr(not(test), allow(dead_code))]
pub fn span(span_id: SpanId) -> Self {
Self::Span { span_id }
}
pub fn stable_prefix(end_exclusive: usize, last_span_id: Option<SpanId>) -> Self {
Self::StablePrefix {
end_exclusive,
last_span_id,
}
}
pub fn last_span_id(&self) -> Option<&SpanId> {
match self {
Self::Span { span_id } => Some(span_id),
Self::StablePrefix { last_span_id, .. } => last_span_id.as_ref(),
}
}
pub fn end_exclusive(&self) -> Option<usize> {
match self {
Self::StablePrefix { end_exclusive, .. } => Some(*end_exclusive),
Self::Span { .. } => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum HintDirective {
Anthropic(AnthropicHintDirective),
OpenAI(OpenAIHintDirective),
}
impl From<AnthropicHintDirective> for HintDirective {
fn from(value: AnthropicHintDirective) -> Self {
Self::Anthropic(value)
}
}
impl From<OpenAIHintDirective> for HintDirective {
fn from(value: OpenAIHintDirective) -> Self {
Self::OpenAI(value)
}
}
pub(crate) fn stable_prefix_target(
prompt_ir: &crate::acg::prompt_ir::PromptIR,
end_exclusive: usize,
) -> HintTarget {
let clamped_end = end_exclusive.min(prompt_ir.blocks.len());
let last_span_id = clamped_end
.checked_sub(1)
.and_then(|index| prompt_ir.blocks.get(index))
.map(|block| block.span_id.clone());
HintTarget::stable_prefix(clamped_end, last_span_id)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum AnthropicHintDirective {
CanonicalizeToolSchemas,
CacheBreakpoint {
target: HintTarget,
scope: SharingScope,
},
ApplyTtl {
ttl: AnthropicCacheTtl,
},
}
#[cfg_attr(not(test), allow(dead_code))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum AnthropicCacheTtl {
OneHour,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum OpenAIHintDirective {
CanonicalizeToolSchemas,
CanonicalizeStablePrefix { target: HintTarget },
}
#[cfg(test)]
#[path = "../../../tests/unit/acg/translation_tests.rs"]
mod tests;