Merge commit 'd0664f3dd6' into ci_mvp

This commit is contained in:
DESKTOP-RTLN3BA\$punk 2026-06-29 17:53:29 -07:00
commit 683a7d3bc2
7 changed files with 19 additions and 152 deletions

View file

@ -10,6 +10,7 @@ from app.utils.document_converters import (
create_document_chunks, create_document_chunks,
embed_text, embed_text,
generate_content_hash, generate_content_hash,
truncate_for_embedding,
) )
from ._helpers import ( from ._helpers import (
@ -73,7 +74,9 @@ async def save_file_document(
if should_skip: if should_skip:
return doc return doc
document_content = f"File: {file_name}\n\n{markdown_content[:4000]}" document_content = (
f"File: {file_name}\n\n{truncate_for_embedding(markdown_content)}"
)
document_embedding = embed_text(document_content) document_embedding = embed_text(document_content)
chunks = await create_document_chunks(markdown_content) chunks = await create_document_chunks(markdown_content)
doc_metadata = {"FILE_NAME": file_name, "ETL_SERVICE": etl_service} doc_metadata = {"FILE_NAME": file_name, "ETL_SERVICE": etl_service}

View file

@ -13,6 +13,7 @@ from app.utils.document_converters import (
create_document_chunks, create_document_chunks,
embed_text, embed_text,
generate_content_hash, generate_content_hash,
truncate_for_embedding,
) )
from ._helpers import ( from ._helpers import (
@ -182,7 +183,9 @@ async def add_received_markdown_file_document(
return doc return doc
# Content changed - continue to update # Content changed - continue to update
summary_content = f"File: {file_name}\n\n{file_in_markdown[:4000]}" summary_content = (
f"File: {file_name}\n\n{truncate_for_embedding(file_in_markdown)}"
)
summary_embedding = embed_text(summary_content) summary_embedding = embed_text(summary_content)
# Process chunks # Process chunks

View file

@ -145,7 +145,7 @@ def _render_block(
code_text = _render_inline_content(content) if content else "" code_text = _render_inline_content(content) if content else ""
lines.append(f"{prefix}```{language}") lines.append(f"{prefix}```{language}")
for code_line in code_text.split("\n"): for code_line in code_text.split("\n"):
lines.append(f"{prefix}{code_line}") lines.append(code_line)
lines.append(f"{prefix}```") lines.append(f"{prefix}```")
elif block_type == "table": elif block_type == "table":

View file

@ -696,7 +696,9 @@ export const InlineMentionEditor = forwardRef<InlineMentionEditorRef, InlineMent
onKeyDown?.(e); onKeyDown?.(e);
if (e.defaultPrevented) return; if (e.defaultPrevented) return;
if (e.key === "Enter" && !e.shiftKey) { // Ignore Enter while an IME composition is active (e.g. confirming a
// Japanese/Chinese/Korean conversion) so it doesn't submit the message.
if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
e.preventDefault(); e.preventDefault();
onSubmit?.(); onSubmit?.();
return; return;

View file

@ -713,6 +713,10 @@ const Composer: FC = () => {
// Arrow / Enter / Escape navigation for the active picker. // Arrow / Enter / Escape navigation for the active picker.
const handleKeyDown = useCallback( const handleKeyDown = useCallback(
(e: React.KeyboardEvent) => { (e: React.KeyboardEvent) => {
// While an IME composition is active (e.g. confirming a Japanese/Chinese/
// Korean conversion), let the Enter/Arrow keys reach the IME instead of
// driving picker navigation/selection.
if (e.nativeEvent.isComposing) return;
if (showPromptPicker) { if (showPromptPicker) {
if (e.key === "ArrowDown") { if (e.key === "ArrowDown") {
e.preventDefault(); e.preventDefault();

View file

@ -99,7 +99,9 @@ export const FreeComposer: FC = () => {
gate("mention documents"); gate("mention documents");
return; return;
} }
if (e.key === "Enter" && !e.shiftKey) { // Ignore Enter while an IME composition is active (e.g. confirming a
// Japanese/Chinese/Korean conversion) so it doesn't submit the message.
if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
e.preventDefault(); e.preventDefault();
if (text.trim()) { if (text.trim()) {
aui.composer().send(); aui.composer().send();

View file

@ -1,147 +0,0 @@
import { z } from "zod";
import { ValidationError } from "../error";
import { baseApiService } from "./base-api.service";
// Request/Response schemas
const createNoteRequest = z.object({
search_space_id: z.number(),
title: z.string().min(1),
source_markdown: z.string().optional(),
});
const createNoteResponse = z.object({
id: z.number(),
title: z.string(),
document_type: z.string(),
content: z.string(),
content_hash: z.string(),
unique_identifier_hash: z.string().nullable(),
document_metadata: z.record(z.string(), z.any()).nullable(),
search_space_id: z.number(),
created_at: z.string(),
updated_at: z.string().nullable(),
});
const getNotesRequest = z.object({
search_space_id: z.number(),
skip: z.number().optional(),
page: z.number().optional(),
page_size: z.number().optional(),
});
const noteItem = z.object({
id: z.number(),
title: z.string(),
document_type: z.string(),
content: z.string(),
content_hash: z.string(),
unique_identifier_hash: z.string().nullable(),
document_metadata: z.record(z.string(), z.any()).nullable(),
search_space_id: z.number(),
created_at: z.string(),
updated_at: z.string().nullable(),
});
const getNotesResponse = z.object({
items: z.array(noteItem),
total: z.number(),
page: z.number(),
page_size: z.number(),
has_more: z.boolean(),
});
const deleteNoteRequest = z.object({
search_space_id: z.number(),
note_id: z.number(),
});
const deleteNoteResponse = z.object({
message: z.string(),
note_id: z.number(),
});
// Type exports
export type CreateNoteRequest = z.infer<typeof createNoteRequest>;
export type CreateNoteResponse = z.infer<typeof createNoteResponse>;
export type GetNotesRequest = z.infer<typeof getNotesRequest>;
export type GetNotesResponse = z.infer<typeof getNotesResponse>;
export type NoteItem = z.infer<typeof noteItem>;
export type DeleteNoteRequest = z.infer<typeof deleteNoteRequest>;
export type DeleteNoteResponse = z.infer<typeof deleteNoteResponse>;
class NotesApiService {
/**
* Create a new note
*/
createNote = async (request: CreateNoteRequest) => {
const parsedRequest = createNoteRequest.safeParse(request);
if (!parsedRequest.success) {
console.error("Invalid request:", parsedRequest.error);
const errorMessage = parsedRequest.error.issues.map((issue) => issue.message).join(", ");
throw new ValidationError(`Invalid request: ${errorMessage}`);
}
const { search_space_id, title, source_markdown } = parsedRequest.data;
// Send both title and source_markdown in request body
const body = {
title,
...(source_markdown !== undefined && { source_markdown }),
};
return baseApiService.post(
`/api/v1/search-spaces/${search_space_id}/notes`,
createNoteResponse,
{ body }
);
};
/**
* Get list of notes
*/
getNotes = async (request: GetNotesRequest) => {
const parsedRequest = getNotesRequest.safeParse(request);
if (!parsedRequest.success) {
console.error("Invalid request:", parsedRequest.error);
const errorMessage = parsedRequest.error.issues.map((issue) => issue.message).join(", ");
throw new ValidationError(`Invalid request: ${errorMessage}`);
}
const { search_space_id, skip, page, page_size } = parsedRequest.data;
// Build query params
const params = new URLSearchParams();
if (skip !== undefined) params.append("skip", String(skip));
if (page !== undefined) params.append("page", String(page));
if (page_size !== undefined) params.append("page_size", String(page_size));
return baseApiService.get(
`/api/v1/search-spaces/${search_space_id}/notes?${params.toString()}`,
getNotesResponse
);
};
/**
* Delete a note
*/
deleteNote = async (request: DeleteNoteRequest) => {
const parsedRequest = deleteNoteRequest.safeParse(request);
if (!parsedRequest.success) {
console.error("Invalid request:", parsedRequest.error);
const errorMessage = parsedRequest.error.issues.map((issue) => issue.message).join(", ");
throw new ValidationError(`Invalid request: ${errorMessage}`);
}
const { search_space_id, note_id } = parsedRequest.data;
return baseApiService.delete(
`/api/v1/search-spaces/${search_space_id}/notes/${note_id}`,
deleteNoteResponse
);
};
}
export const notesApiService = new NotesApiService();