mirror of
https://github.com/perstarkse/minne.git
synced 2026-05-01 21:24:17 +02:00
fix: own implementation of stream parser
This commit is contained in:
@@ -21,7 +21,6 @@ axum_typed_multipart = "0.12.1"
|
||||
futures = "0.3.31"
|
||||
tempfile = "3.12.0"
|
||||
async-stream = "0.3.6"
|
||||
json-stream-parser = "0.1.4"
|
||||
minijinja = { version = "2.5.0", features = ["loader", "multi_template"] }
|
||||
minijinja-autoreload = "2.5.0"
|
||||
minijinja-embed = { version = "2.8.0" }
|
||||
|
||||
@@ -21,7 +21,6 @@ use futures::{
|
||||
stream::{self, once},
|
||||
Stream, StreamExt, TryStreamExt,
|
||||
};
|
||||
use json_stream_parser::JsonStreamParser;
|
||||
use minijinja::Value;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::from_str;
|
||||
@@ -29,14 +28,17 @@ use surrealdb::{engine::any::Any, Surreal};
|
||||
use tokio::sync::{mpsc::channel, Mutex};
|
||||
use tracing::{debug, error};
|
||||
|
||||
use common::storage::{
|
||||
db::SurrealDbClient,
|
||||
types::{
|
||||
conversation::Conversation,
|
||||
message::{Message, MessageRole},
|
||||
system_settings::SystemSettings,
|
||||
user::User,
|
||||
use common::{
|
||||
storage::{
|
||||
db::SurrealDbClient,
|
||||
types::{
|
||||
conversation::Conversation,
|
||||
message::{Message, MessageRole},
|
||||
system_settings::SystemSettings,
|
||||
user::User,
|
||||
},
|
||||
},
|
||||
utils::stream_parser::JsonStreamParser,
|
||||
};
|
||||
|
||||
use crate::html_state::HtmlState;
|
||||
|
||||
@@ -24,4 +24,37 @@
|
||||
}
|
||||
}
|
||||
)
|
||||
// Add listener for after content is settled
|
||||
document.body.addEventListener('htmx:afterSettle', function (evt) {
|
||||
// Check if the settled element has our specific class
|
||||
// evt.detail.target might be the container, elt is often the element *making* the request
|
||||
// We need the element *receiving* the swap. Let's target specifically.
|
||||
const messageId = "{{user_message.id}}"; // Get the ID from the template context
|
||||
const targetBubble = document.querySelector(`.ai-message-content-${messageId}`);
|
||||
|
||||
// Ensure we have the marked library and the target exists
|
||||
if (targetBubble && typeof marked !== 'undefined') {
|
||||
// Get the raw text content (which includes previously streamed parts)
|
||||
// Exclude the spinner if it's still somehow there, though it should be hidden.
|
||||
let rawContent = '';
|
||||
targetBubble.childNodes.forEach(node => {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
rawContent += node.textContent;
|
||||
} else if (node.nodeType === Node.ELEMENT_NODE && !node.classList.contains('loading')) {
|
||||
// In case HTMX wraps text in spans or something unexpected later
|
||||
rawContent += node.textContent;
|
||||
}
|
||||
});
|
||||
console.log(rawContent);
|
||||
// Sanitize BEFORE inserting potentially harmful HTML from Markdown
|
||||
// It's better to sanitize *after* rendering if using DOMPurify
|
||||
targetBubble.innerHTML = marked.parse(rawContent);
|
||||
// Optional: Sanitize with DOMPurify *after* parsing for security
|
||||
// if (typeof DOMPurify !== 'undefined') {
|
||||
// targetBubble.innerHTML = DOMPurify.sanitize(marked.parse(rawContent));
|
||||
// } else {
|
||||
// targetBubble.innerHTML = marked.parse(rawContent); // Use with caution if markdown source isn't trusted
|
||||
// }
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -19,6 +19,7 @@
|
||||
<script src="/assets/htmx-ext-sse.js" defer></script>
|
||||
<script src="/assets/theme-toggle.js" defer></script>
|
||||
<script src="/assets/toast.js" defer></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
|
||||
<!-- Icons -->
|
||||
<link rel="icon" href="/assets/icon/favicon.ico">
|
||||
|
||||
Reference in New Issue
Block a user