Files
ss-tools/frontend/src/lib/components/assistant/MarkdownRenderer.svelte
busya f87ebf5d4b feat(agent): Gradio-powered LangGraph agent chat with streaming, tool calls, file upload, conversation persistence
- Gradio 5.50.0 ChatInterface with type='messages' streaming
- LangGraph create_react_agent with InMemorySaver checkpointer
- 4 @tool functions: search_dashboards, get_health_summary, list_environments, get_task_status
- Structured ChatMessage metadata (7 discriminator types: stream_token, tool_start/end/error, confirm_required, confirm_resolved, error)
- HITL resume via second submit() with interrupt_before/Command
- Dual-identity RBAC: service JWT + user JWT for tool calls
- File upload (10 MB limit, pdfplumber/xlsx/JSON parser)
- Conversation persistence via POST /api/agent/conversations/save
- REST API: list, history, archive conversations; multi-tab gate; LLM config
- LLM provider selection via Admin -> LLM Settings (assistant_planner_provider)
- Svelte 5 AgentChatModel with stream event queue, dedup, stream_status watcher
- MarkdownRenderer using svelte-markdown with semantic Tailwind tokens
- ToolCallCard (3 states: executing/completed/failed)
- ConversationList with search, date grouping, infinite scroll
- ConnectionIndicator with Gradio health status
- /agent route with two-column layout
- Vite proxy /api/agent/gradio -> Gradio SSE
- Fixed: not_() SQLAlchemy operator, route collision with _admin_routes
- Fixed: conversation_id -> id normalization, .pyc cache staleness
- Fixed: event.data array parsing (Gradio returns [jsonStr, null])
- Requirements pinned: gradio==5.50.0, pydantic>=2.7,<=2.12.3
2026-06-10 10:27:19 +03:00

136 lines
3.6 KiB
Svelte

<!-- #region AssistantChat.MarkdownRenderer [C:2] [TYPE Component] [SEMANTICS assistant,chat,markdown,rendering] -->
<!-- @ingroup AgentChat -->
<!-- @BRIEF Renders markdown text using svelte-markdown with semantic Tailwind prose styles. -->
<!-- @LAYER UI -->
<!-- @RELATION DEPENDS_ON -> [EXT:svelte-markdown] -->
<!-- @UX_STATE Rendered -> Markdown rendered with proper headings, lists, code blocks, links. -->
<!-- @UX_STATE Empty -> Empty string renders nothing. -->
<script lang="ts">
import SvelteMarkdown from "svelte-markdown";
let { source = "" } = $props();
</script>
{#if source}
<div class="markdown-text">
<SvelteMarkdown {source} />
</div>
{/if}
<style>
/* svelte-markdown renderers use semantic HTML tags — style via cascade */
.markdown-text :global(h1),
.markdown-text :global(h2),
.markdown-text :global(h3) {
font-weight: 600;
color: var(--color-text, #1a1a2e);
margin-top: 0.75rem;
margin-bottom: 0.375rem;
}
.markdown-text :global(h1) {
font-size: 1.125rem;
line-height: 1.5rem;
}
.markdown-text :global(h2) {
font-size: 1rem;
line-height: 1.375rem;
}
.markdown-text :global(h3) {
font-size: 0.875rem;
line-height: 1.25rem;
}
.markdown-text :global(p) {
font-size: 0.875rem;
line-height: 1.5;
color: var(--color-text, #1a1a2e);
margin-bottom: 0.375rem;
}
.markdown-text :global(ul),
.markdown-text :global(ol) {
list-style-position: inside;
font-size: 0.875rem;
line-height: 1.5;
color: var(--color-text, #1a1a2e);
margin-bottom: 0.375rem;
padding-left: 0;
}
.markdown-text :global(ul) {
list-style-type: disc;
}
.markdown-text :global(ol) {
list-style-type: decimal;
}
.markdown-text :global(li) {
margin-bottom: 0.125rem;
}
.markdown-text :global(code) {
border-radius: 0.25rem;
background-color: var(--color-surface-muted, #f3f4f6);
padding: 0.125rem 0.375rem;
font-size: 0.75rem;
font-family: ui-monospace, monospace;
color: var(--color-text, #1a1a2e);
}
.markdown-text :global(pre) {
border-radius: 0.5rem;
background-color: var(--color-surface-muted, #f3f4f6);
padding: 0.75rem;
overflow-x: auto;
margin-bottom: 0.5rem;
}
.markdown-text :global(pre code) {
background: none;
padding: 0;
font-size: 0.75rem;
}
.markdown-text :global(a) {
color: var(--color-primary, #2563eb);
text-decoration: underline;
}
.markdown-text :global(a:hover) {
color: var(--color-primary-hover, #1d4ed8);
}
.markdown-text :global(strong) {
font-weight: 600;
color: var(--color-text, #1a1a2e);
}
.markdown-text :global(em) {
font-style: italic;
color: var(--color-text, #1a1a2e);
}
.markdown-text :global(hr) {
border: none;
border-top: 1px solid var(--color-border, #e5e7eb);
margin: 0.75rem 0;
}
.markdown-text :global(blockquote) {
border-left: 3px solid var(--color-primary, #2563eb);
padding-left: 0.75rem;
margin: 0.5rem 0;
color: var(--color-text-muted, #6b7280);
font-style: italic;
}
.markdown-text :global(table) {
width: 100%;
border-collapse: collapse;
margin-bottom: 0.5rem;
font-size: 0.8125rem;
}
.markdown-text :global(th),
.markdown-text :global(td) {
border: 1px solid var(--color-border, #e5e7eb);
padding: 0.375rem 0.5rem;
text-align: left;
}
.markdown-text :global(th) {
background-color: var(--color-surface-muted, #f3f4f6);
font-weight: 600;
}
.markdown-text :global(img) {
max-width: 100%;
border-radius: 0.375rem;
margin: 0.5rem 0;
}
</style>
<!-- #endregion AssistantChat.MarkdownRenderer -->