kopitiam_ai/message.rs
1use serde::{Deserialize, Serialize};
2
3/// Who authored a [`Message`] in a [`CompletionRequest`].
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
5#[serde(rename_all = "snake_case")]
6pub enum Role {
7 /// Instructions/context assembled by `kopitiam-workflow`'s context
8 /// builder — never hand-typed by a human in this crate.
9 System,
10 /// The task or question being posed to the model.
11 User,
12 /// A prior model response, present when a workflow is multi-turn.
13 Assistant,
14}
15
16/// One turn in a [`CompletionRequest`].
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18pub struct Message {
19 pub role: Role,
20 pub content: String,
21}
22
23impl Message {
24 pub fn system(content: impl Into<String>) -> Self {
25 Self { role: Role::System, content: content.into() }
26 }
27
28 pub fn user(content: impl Into<String>) -> Self {
29 Self { role: Role::User, content: content.into() }
30 }
31
32 pub fn assistant(content: impl Into<String>) -> Self {
33 Self { role: Role::Assistant, content: content.into() }
34 }
35}
36
37/// A request to a [`crate::ModelAdapter`].
38///
39/// This is deliberately shaped like a chat completion, since every adapter
40/// KOPITIAM plans to support (local Qwen, Claude, GPT, Gemini) speaks that
41/// shape natively. `messages` is expected to already contain whatever
42/// context `kopitiam-workflow`'s `ContextBuilder` assembled — adapters must
43/// not go looking for additional context themselves.
44#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
45pub struct CompletionRequest {
46 pub messages: Vec<Message>,
47 /// Upper bound on generated tokens, if the caller wants one. `None`
48 /// leaves the limit to the adapter/model's own default.
49 #[serde(default)]
50 pub max_tokens: Option<u32>,
51}
52
53impl CompletionRequest {
54 pub fn new(messages: impl IntoIterator<Item = Message>) -> Self {
55 Self { messages: messages.into_iter().collect(), max_tokens: None }
56 }
57
58 pub fn with_max_tokens(mut self, max_tokens: u32) -> Self {
59 self.max_tokens = Some(max_tokens);
60 self
61 }
62}
63
64/// A [`crate::ModelAdapter`]'s reply to a [`CompletionRequest`].
65#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
66pub struct CompletionResponse {
67 pub content: String,
68 /// Identifies which model actually produced `content` (e.g.
69 /// `"qwen2.5-coder-7b"`, `"claude-sonnet-5"`), independent of the
70 /// adapter's own [`crate::ModelAdapter::name`] — an adapter may serve
71 /// more than one model.
72 pub model: String,
73}