Skip to main content

ferrin_core/middleware/builtin/
default_instructions.rs

1//! Default system instructions.
2
3use ferrin_spec::BoxFuture;
4use ferrin_spec::CallOptions;
5use ferrin_spec::error::ProviderError;
6use ferrin_spec::language_model::prompt::PromptMessage;
7
8use crate::middleware::LanguageModelMiddleware;
9use crate::middleware::MiddlewareContext;
10use crate::prompt::Instructions;
11
12/// Middleware created by [`default_instructions`].
13#[derive(Debug, Clone)]
14pub struct DefaultInstructions {
15    instructions: Instructions,
16}
17
18/// Prepends `instructions` as a system message when the prompt has no
19/// system message.
20#[must_use]
21pub fn default_instructions(instructions: impl Into<Instructions>) -> DefaultInstructions {
22    DefaultInstructions {
23        instructions: instructions.into(),
24    }
25}
26
27impl LanguageModelMiddleware for DefaultInstructions {
28    fn transform_params<'a>(
29        &'a self,
30        mut options: CallOptions,
31        _ctx: MiddlewareContext<'a>,
32    ) -> BoxFuture<'a, Result<CallOptions, ProviderError>> {
33        let has_system = options
34            .prompt
35            .iter()
36            .any(|message| matches!(message, PromptMessage::System { .. }));
37        if !has_system {
38            let message = self.instructions.clone().into_message();
39            options.prompt.insert(
40                0,
41                PromptMessage::System {
42                    content: message.content,
43                    provider_options: message.provider_options,
44                },
45            );
46        }
47        Box::pin(async move { Ok(options) })
48    }
49}