Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
533b025f6b | ||
|
|
8b12bdf2b3 | ||
|
|
d4ddd29660 | ||
|
|
941e5f0bc4 |
1
.github/workflows/workflow.yml
vendored
1
.github/workflows/workflow.yml
vendored
@@ -53,3 +53,4 @@ jobs:
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
file: Dockerfile.ci
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
# (2024-09-22) Version 1.1.1
|
||||
|
||||
- (**Bugfix**) Fix thread messages being lost due to lack of pagination support ([d4ddd29660](https://github.com/etkecc/baibot/commit/d4ddd29660d9f51d248119dd6032e68ab29e7d35)) - fixes [issue #13](https://github.com/etkecc/baibot/issues/13)
|
||||
|
||||
- (**Bugfix**) Fix Anthropic conversations getting stuck when being impatient and sending multiple consecutive messages ([8b12bdf2b3](https://github.com/etkecc/baibot/commit/8b12bdf2b3196abea0e8db33d7c50fff48341cb9)) - fixes [issue #13](https://github.com/etkecc/baibot/issues/13)
|
||||
|
||||
|
||||
# (2024-09-21) Version 1.1.0
|
||||
|
||||
- (**Feature**) Adds support for [prompt variables](./docs/configuration/text-generation.md#️-prompt-override) (date/time, bot name, model id) ([2a5a2d6a4d](https://github.com/etkecc/baibot/commit/2a5a2d6a4dbf5fd7cb504ac07d4187fdc32ae395))
|
||||
- (**Feature**) Adds support for [prompt variables](./docs/configuration/text-generation.md#️-prompt-override) (date/time, bot name, model id) ([2a5a2d6a4d](https://github.com/etkecc/baibot/commit/2a5a2d6a4dbf5fd7cb504ac07d4187fdc32ae395)) - fixes [issue #10](https://github.com/etkecc/baibot/issues/10)
|
||||
|
||||
- (**Improvement**) [Dockerfile](./Dockerfile) changes to produce ~20MB smaller container images ([354063abb7](https://github.com/etkecc/baibot/commit/354063abb79035069bd3b26c53214874e9cdd95d))
|
||||
|
||||
|
||||
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -297,7 +297,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "baibot"
|
||||
version = "1.1.0"
|
||||
version = "1.1.1"
|
||||
dependencies = [
|
||||
"anthropic-rs",
|
||||
"anyhow",
|
||||
@@ -2132,14 +2132,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mxlink"
|
||||
version = "1.2.1"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1aa513435471af3e1131dc18a1f4eb2e6fb42c11308f1be9b4f0316b3809e32"
|
||||
checksum = "f5f7234edcd534b11daad1641a148b8bfcae417deb6e6bff564ae69b3d3c4678"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"chacha20poly1305",
|
||||
"hex",
|
||||
"js_int",
|
||||
"matrix-sdk",
|
||||
"mime",
|
||||
"quick_cache",
|
||||
|
||||
@@ -7,7 +7,7 @@ license = "AGPL-3.0-or-later"
|
||||
readme = "README.md"
|
||||
keywords = ["matrix", "chat", "bot", "AI", "LLM"]
|
||||
include = ["/etc/assets/baibot-torso-768.png", "/src", "/README.md", "/CHANGELOG.md", "/LICENSE"]
|
||||
version = "1.1.0"
|
||||
version = "1.1.1"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
@@ -23,7 +23,7 @@ chrono = { version = "0.4.*", default-features = false, features = ["std", "now"
|
||||
# We'd rather not depend on this, but we cannot use the ruma-events EventContent macro without it.
|
||||
matrix-sdk = { version = "0.7.1", default-features = false }
|
||||
mxidwc = "1.0.*"
|
||||
mxlink = ">=1.2.1"
|
||||
mxlink = ">=1.3.0"
|
||||
etke_openai_api_rust = "0.1.*"
|
||||
quick_cache = "0.6.*"
|
||||
regex = "1.10.*"
|
||||
|
||||
35
Dockerfile.ci
Normal file
35
Dockerfile.ci
Normal file
@@ -0,0 +1,35 @@
|
||||
#######################################
|
||||
# #
|
||||
# Stage 1: building #
|
||||
# #
|
||||
#######################################
|
||||
|
||||
FROM docker.io/rust:1.81.0-slim-bookworm AS build
|
||||
|
||||
RUN apt-get update && apt-get install -y build-essential pkg-config libssl-dev libsqlite3-dev
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . /app
|
||||
|
||||
RUN cargo build --release
|
||||
|
||||
#######################################
|
||||
# #
|
||||
# Stage 2: packaging #
|
||||
# #
|
||||
#######################################
|
||||
|
||||
FROM docker.io/debian:bookworm-slim
|
||||
|
||||
RUN apt-get update && apt-get install -y ca-certificates sqlite3 && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=build /app/target/release/baibot .
|
||||
|
||||
ENTRYPOINT ["/bin/sh", "-c"]
|
||||
|
||||
CMD ["/app/baibot"]
|
||||
@@ -111,6 +111,15 @@ impl ControllerTrait for Controller {
|
||||
})
|
||||
};
|
||||
|
||||
// Avoid the situation where multiple user or assistant messages are sent consecutively,
|
||||
// to avoid errors like:
|
||||
// > API error: Error response: error Api error: invalid_request_error messages: roles must alternate between "user" and "assistant", but found multiple "user" roles in a row
|
||||
// as reported here: https://github.com/etkecc/baibot/issues/13
|
||||
//
|
||||
// As https://docs.anthropic.com/en/api/messages says:
|
||||
// > Our models are trained to operate on alternating user and assistant conversational turns.
|
||||
let conversation = conversation.combine_consecutive_messages();
|
||||
|
||||
let mut conversation_messages = conversation.messages;
|
||||
|
||||
if params.context_management_enabled {
|
||||
|
||||
@@ -15,3 +15,94 @@ pub struct Message {
|
||||
pub struct Conversation {
|
||||
pub messages: Vec<Message>,
|
||||
}
|
||||
|
||||
impl Conversation {
|
||||
/// Combine consecutive messages by the same author into a single message.
|
||||
///
|
||||
/// Certain models (like Anthropic) cannot tolerate consecutive messages by the same author,
|
||||
/// so combining them helps avoid issues.
|
||||
/// See: https://github.com/etkecc/baibot/issues/13
|
||||
pub fn combine_consecutive_messages(&self) -> Conversation {
|
||||
// We'll likely get fewer messages, but let's reserve the maximum we expect.
|
||||
let mut new_messages = Vec::with_capacity(self.messages.len());
|
||||
let mut last_seen_author: Option<Author> = None;
|
||||
|
||||
for message in &self.messages {
|
||||
let Some(last_seen_author_clone) = last_seen_author.clone() else {
|
||||
last_seen_author = Some(message.author.clone());
|
||||
new_messages.push(message.clone());
|
||||
continue;
|
||||
};
|
||||
|
||||
if message.author != last_seen_author_clone {
|
||||
last_seen_author = Some(message.author.clone());
|
||||
new_messages.push(message.clone());
|
||||
continue;
|
||||
}
|
||||
|
||||
new_messages.last_mut().unwrap().message_text.push('\n');
|
||||
new_messages
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.message_text
|
||||
.push_str(&message.message_text);
|
||||
}
|
||||
|
||||
Conversation {
|
||||
messages: new_messages,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn combine_consecutive_messages() {
|
||||
let conversation = Conversation {
|
||||
messages: vec![
|
||||
Message {
|
||||
author: Author::User,
|
||||
message_text: "Hello".to_string(),
|
||||
},
|
||||
Message {
|
||||
author: Author::User,
|
||||
message_text: "How are you?".to_string(),
|
||||
},
|
||||
Message {
|
||||
author: Author::User,
|
||||
message_text: "I'm OK, btw.".to_string(),
|
||||
},
|
||||
Message {
|
||||
author: Author::Assistant,
|
||||
message_text: "Hi there!".to_string(),
|
||||
},
|
||||
Message {
|
||||
author: Author::Assistant,
|
||||
message_text: "I'm doing well, thank you.".to_string(),
|
||||
},
|
||||
Message {
|
||||
author: Author::User,
|
||||
message_text: "That's great!".to_string(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let conversation = conversation.combine_consecutive_messages();
|
||||
|
||||
assert_eq!(conversation.messages.len(), 3);
|
||||
assert_eq!(conversation.messages[0].author, Author::User);
|
||||
assert_eq!(
|
||||
conversation.messages[0].message_text,
|
||||
"Hello\nHow are you?\nI'm OK, btw."
|
||||
);
|
||||
assert_eq!(conversation.messages[1].author, Author::Assistant);
|
||||
assert_eq!(
|
||||
conversation.messages[1].message_text,
|
||||
"Hi there!\nI'm doing well, thank you."
|
||||
);
|
||||
assert_eq!(conversation.messages[2].author, Author::User);
|
||||
assert_eq!(conversation.messages[2].message_text, "That's great!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ use mxlink::matrix_sdk::{
|
||||
},
|
||||
Room,
|
||||
};
|
||||
use mxlink::{MatrixLink, ThreadInfo};
|
||||
use mxlink::{MatrixLink, ThreadGetMessagesParams, ThreadInfo};
|
||||
|
||||
use super::{MatrixMessage, MatrixMessageProcessingParams, MatrixMessageType, RoomEventFetcher};
|
||||
use crate::entity::{MessagePayload, ThreadContext, ThreadContextFirstMessage};
|
||||
@@ -23,7 +23,10 @@ pub async fn get_matrix_messages_in_thread(
|
||||
room: &Room,
|
||||
thread_id: OwnedEventId,
|
||||
) -> Result<Vec<MatrixMessage>, mxlink::matrix_sdk::Error> {
|
||||
let messages_native = matrix_link.threads().get_messages(room, thread_id).await?;
|
||||
let messages_native = matrix_link
|
||||
.threads()
|
||||
.get_messages(room, thread_id, ThreadGetMessagesParams::default())
|
||||
.await?;
|
||||
|
||||
let mut messages: Vec<MatrixMessage> = Vec::new();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user