Add interactive Chat with LLM interface to Web UI

Features:
- Real-time streaming chat interface (ChatGPT-like experience)
- Model selection: Text model (fast) or Vision model (image analysis)
- System prompt toggle: Chat with Miku's personality or raw LLM
- Mood selector: Choose from 14 different emotional states
- Full context integration: Uses complete miku_lore.txt, miku_prompt.txt, and miku_lyrics.txt
- Conversation memory: Maintains chat history throughout session
- Image upload support for vision model
- Horizontal scrolling tabs for responsive design
- Clear chat history functionality
- SSE (Server-Sent Events) for streaming responses
- Keyboard shortcuts (Ctrl+Enter to send)

Technical changes:
- Added POST /chat/stream endpoint in api.py with streaming support
- Updated ChatMessage model with mood, conversation_history, and image_data
- Integrated context_manager for proper Miku personality context
- Added Chat with LLM tab to index.html
- Implemented JavaScript streaming client with EventSource-like handling
- Added CSS for chat messages, typing indicators, and animations
- Made tab navigation horizontally scrollable for narrow viewports
This commit is contained in:
2025-12-13 00:23:03 +02:00
parent 65e6c3e7ea
commit bb82b7f146
23 changed files with 2926 additions and 8 deletions

296
CHAT_INTERFACE_FEATURE.md Normal file
View File

@@ -0,0 +1,296 @@
# Chat Interface Feature Documentation
## Overview
A new **"Chat with LLM"** tab has been added to the Miku bot Web UI, allowing you to chat directly with the language models with full streaming support (similar to ChatGPT).
## Features
### 1. Model Selection
- **💬 Text Model (Fast)**: Chat with the text-based LLM for quick conversations
- **👁️ Vision Model (Images)**: Use the vision model to analyze and discuss images
### 2. System Prompt Options
- **✅ Use Miku Personality**: Attach the standard Miku personality system prompt
- Text model: Gets the full Miku character prompt (same as `query_llama`)
- Vision model: Gets a simplified Miku-themed image analysis prompt
- **❌ Raw LLM (No Prompt)**: Chat directly with the base LLM without any personality
- Great for testing raw model responses
- No character constraints
### 3. Real-time Streaming
- Messages stream in character-by-character like ChatGPT
- Shows typing indicator while waiting for response
- Smooth, responsive interface
### 4. Vision Model Support
- Upload images when using the vision model
- Image preview before sending
- Analyze images with Miku's personality or raw vision capabilities
### 5. Chat Management
- Clear chat history button
- Timestamps on all messages
- Color-coded messages (user vs assistant)
- Auto-scroll to latest message
- Keyboard shortcut: **Ctrl+Enter** to send messages
## Technical Implementation
### Backend (api.py)
#### New Endpoint: `POST /chat/stream`
```python
# Accepts:
{
"message": "Your chat message",
"model_type": "text" | "vision",
"use_system_prompt": true | false,
"image_data": "base64_encoded_image" (optional, for vision model)
}
# Returns: Server-Sent Events (SSE) stream
data: {"content": "streamed text chunk"}
data: {"done": true}
data: {"error": "error message"}
```
**Key Features:**
- Uses Server-Sent Events (SSE) for streaming
- Supports both `TEXT_MODEL` and `VISION_MODEL` from globals
- Dynamically switches system prompts based on configuration
- Integrates with llama.cpp's streaming API
### Frontend (index.html)
#### New Tab: "💬 Chat with LLM"
Located in the main navigation tabs (tab6)
**Components:**
1. **Configuration Panel**
- Radio buttons for model selection
- Radio buttons for system prompt toggle
- Image upload section (shows/hides based on model)
- Clear chat history button
2. **Chat Messages Container**
- Scrollable message history
- Animated message appearance
- Typing indicator during streaming
- Color-coded messages with timestamps
3. **Input Area**
- Multi-line text input
- Send button with loading state
- Keyboard shortcuts
**JavaScript Functions:**
- `sendChatMessage()`: Handles message sending and streaming reception
- `toggleChatImageUpload()`: Shows/hides image upload for vision model
- `addChatMessage()`: Adds messages to chat display
- `showTypingIndicator()` / `hideTypingIndicator()`: Typing animation
- `clearChatHistory()`: Clears all messages
- `handleChatKeyPress()`: Keyboard shortcuts
## Usage Guide
### Basic Text Chat with Miku
1. Go to "💬 Chat with LLM" tab
2. Ensure "💬 Text Model" is selected
3. Ensure "✅ Use Miku Personality" is selected
4. Type your message and click "📤 Send" (or press Ctrl+Enter)
5. Watch as Miku's response streams in real-time!
### Raw LLM Testing
1. Select "💬 Text Model"
2. Select "❌ Raw LLM (No Prompt)"
3. Chat directly with the base language model without personality constraints
### Vision Model Chat
1. Select "👁️ Vision Model"
2. Click "Upload Image" and select an image
3. Type a message about the image (e.g., "What do you see in this image?")
4. Click "📤 Send"
5. The vision model will analyze the image and respond
### Vision Model with Miku Personality
1. Select "👁️ Vision Model"
2. Keep "✅ Use Miku Personality" selected
3. Upload an image
4. Miku will analyze and comment on the image with her cheerful personality!
## System Prompts
### Text Model (with Miku personality)
Uses the same comprehensive system prompt as `query_llama()`:
- Full Miku character context
- Current mood integration
- Character consistency rules
- Natural conversation guidelines
### Vision Model (with Miku personality)
Simplified prompt optimized for image analysis:
```
You are Hatsune Miku analyzing an image. Describe what you see naturally
and enthusiastically as Miku would. Be detailed but conversational.
React to what you see with Miku's cheerful, playful personality.
```
### No System Prompt
Both models respond without personality constraints when this option is selected.
## Streaming Technology
The interface uses **Server-Sent Events (SSE)** for real-time streaming:
- Backend sends chunked responses from llama.cpp
- Frontend receives and displays chunks as they arrive
- Smooth, ChatGPT-like experience
- Works with both text and vision models
## UI/UX Features
### Message Styling
- **User messages**: Green accent, right-aligned feel
- **Assistant messages**: Blue accent, left-aligned feel
- **Error messages**: Red accent with error icon
- **Fade-in animation**: Smooth appearance for new messages
### Responsive Design
- Chat container scrolls automatically
- Image preview for vision model
- Loading states on buttons
- Typing indicators
- Custom scrollbar styling
### Keyboard Shortcuts
- **Ctrl+Enter**: Send message quickly
- **Tab**: Navigate between input fields
## Configuration Options
All settings are preserved during the chat session:
- Model type (text/vision)
- System prompt toggle (Miku/Raw)
- Uploaded image (for vision model)
Settings do NOT persist after page refresh (fresh session each time).
## Error Handling
The interface handles various errors gracefully:
- Connection failures
- Model errors
- Invalid image files
- Empty messages
- Timeout issues
All errors are displayed in the chat with clear error messages.
## Performance Considerations
### Text Model
- Fast responses (typically 1-3 seconds)
- Streaming starts almost immediately
- Low latency
### Vision Model
- Slower due to image processing
- First token may take 3-10 seconds
- Streaming continues once started
- Image is sent as base64 (efficient)
## Development Notes
### File Changes
1. **`bot/api.py`**
- Added `from fastapi.responses import StreamingResponse`
- Added `ChatMessage` Pydantic model
- Added `POST /chat/stream` endpoint with SSE support
2. **`bot/static/index.html`**
- Added tab6 button in navigation
- Added complete chat interface HTML
- Added CSS styles for chat messages and animations
- Added JavaScript functions for chat functionality
### Dependencies
- Uses existing `aiohttp` for HTTP streaming
- Uses existing `globals.TEXT_MODEL` and `globals.VISION_MODEL`
- Uses existing `globals.LLAMA_URL` for llama.cpp connection
- No new dependencies required!
## Future Enhancements (Ideas)
Potential improvements for future versions:
- [ ] Save/load chat sessions
- [ ] Export chat history to file
- [ ] Multi-user chat history (separate sessions per user)
- [ ] Temperature and max_tokens controls
- [ ] Model selection dropdown (if multiple models available)
- [ ] Token count display
- [ ] Voice input support
- [ ] Markdown rendering in responses
- [ ] Code syntax highlighting
- [ ] Copy message button
- [ ] Regenerate response button
## Troubleshooting
### "No response received from LLM"
- Check if llama.cpp server is running
- Verify `LLAMA_URL` in globals is correct
- Check bot logs for connection errors
### "Failed to read image file"
- Ensure image is valid format (JPEG, PNG, GIF)
- Check file size (large images may cause issues)
- Try a different image
### Streaming not working
- Check browser console for JavaScript errors
- Verify SSE is not blocked by proxy/firewall
- Try refreshing the page
### Model not responding
- Check if correct model is loaded in llama.cpp
- Verify model type matches what's configured
- Check llama.cpp logs for errors
## API Reference
### POST /chat/stream
**Request Body:**
```json
{
"message": "string", // Required: User's message
"model_type": "text|vision", // Required: Which model to use
"use_system_prompt": boolean, // Required: Whether to add system prompt
"image_data": "string|null" // Optional: Base64 image for vision model
}
```
**Response:**
```
Content-Type: text/event-stream
data: {"content": "Hello"}
data: {"content": " there"}
data: {"content": "!"}
data: {"done": true}
```
**Error Response:**
```
data: {"error": "Error message here"}
```
## Conclusion
The Chat Interface provides a powerful, user-friendly way to:
- Test LLM responses interactively
- Experiment with different prompting strategies
- Analyze images with vision models
- Chat with Miku's personality in real-time
- Debug and understand model behavior
All with a smooth, modern streaming interface that feels like ChatGPT! 🎉

148
CHAT_QUICK_START.md Normal file
View File

@@ -0,0 +1,148 @@
# Chat Interface - Quick Start Guide
## 🚀 Quick Start
### Access the Chat Interface
1. Open the Miku Control Panel in your browser
2. Click on the **"💬 Chat with LLM"** tab
3. Start chatting!
## 📋 Configuration Options
### Model Selection
- **💬 Text Model**: Fast text conversations
- **👁️ Vision Model**: Image analysis
### System Prompt
- **✅ Use Miku Personality**: Chat with Miku's character
- **❌ Raw LLM**: Direct LLM without personality
## 💡 Common Use Cases
### 1. Chat with Miku
```
Model: Text Model
System Prompt: Use Miku Personality
Message: "Hi Miku! How are you feeling today?"
```
### 2. Test Raw LLM
```
Model: Text Model
System Prompt: Raw LLM
Message: "Explain quantum physics"
```
### 3. Analyze Images with Miku
```
Model: Vision Model
System Prompt: Use Miku Personality
Upload: [your image]
Message: "What do you think of this image?"
```
### 4. Raw Image Analysis
```
Model: Vision Model
System Prompt: Raw LLM
Upload: [your image]
Message: "Describe this image in detail"
```
## ⌨️ Keyboard Shortcuts
- **Ctrl+Enter**: Send message
## 🎨 Features
- ✅ Real-time streaming (like ChatGPT)
- ✅ Image upload for vision model
- ✅ Color-coded messages
- ✅ Timestamps
- ✅ Typing indicators
- ✅ Auto-scroll
- ✅ Clear chat history
## 🔧 System Prompts
### Text Model with Miku
- Full Miku personality
- Current mood awareness
- Character consistency
### Vision Model with Miku
- Miku analyzing images
- Cheerful, playful descriptions
### No System Prompt
- Direct LLM responses
- No character constraints
## 📊 Message Types
### User Messages (Green)
- Your input
- Right-aligned appearance
### Assistant Messages (Blue)
- Miku/LLM responses
- Left-aligned appearance
- Streams in real-time
### Error Messages (Red)
- Connection errors
- Model errors
- Clear error descriptions
## 🎯 Tips
1. **Use Ctrl+Enter** for quick sending
2. **Select model first** before uploading images
3. **Clear history** to start fresh conversations
4. **Toggle system prompt** to compare responses
5. **Wait for streaming** to complete before sending next message
## 🐛 Troubleshooting
### No response?
- Check if llama.cpp is running
- Verify network connection
- Check browser console
### Image not working?
- Switch to Vision Model
- Use valid image format (JPG, PNG)
- Check file size
### Slow responses?
- Vision model is slower than text
- Wait for streaming to complete
- Check llama.cpp load
## 📝 Examples
### Example 1: Personality Test
**With Miku Personality:**
> User: "What's your favorite song?"
> Miku: "Oh, I love so many songs! But if I had to choose, I'd say 'World is Mine' holds a special place in my heart! It really captures that fun, playful energy that I love! ✨"
**Without System Prompt:**
> User: "What's your favorite song?"
> LLM: "I don't have personal preferences as I'm an AI language model..."
### Example 2: Image Analysis
**With Miku Personality:**
> User: [uploads sunset image] "What do you see?"
> Miku: "Wow! What a beautiful sunset! The sky is painted with such gorgeous oranges and pinks! It makes me want to write a song about it! The way the colors blend together is so dreamy and romantic~ 🌅💕"
**Without System Prompt:**
> User: [uploads sunset image] "What do you see?"
> LLM: "This image shows a sunset landscape. The sky displays orange and pink hues. The sun is setting on the horizon. There are silhouettes of trees in the foreground."
## 🎉 Enjoy Chatting!
Have fun experimenting with different combinations of:
- Text vs Vision models
- With vs Without system prompts
- Different types of questions
- Various images (for vision model)
The streaming interface makes it feel just like ChatGPT! 🚀

View File

@@ -7,7 +7,8 @@ from fastapi import (
File,
Form
)
from typing import List
from fastapi.responses import StreamingResponse
from typing import List, Optional
from pydantic import BaseModel
import globals
from server_manager import server_manager
@@ -36,6 +37,7 @@ import subprocess
import io
import discord
import aiofiles
import aiohttp
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse, PlainTextResponse
import os
@@ -1512,6 +1514,184 @@ async def get_v2_status():
except Exception as e:
return {"status": "error", "message": str(e)}
# ========== Chat Interface Endpoints ==========
class ChatMessage(BaseModel):
message: str
model_type: str = "text" # "text" or "vision"
use_system_prompt: bool = True
image_data: Optional[str] = None # Base64 encoded image for vision model
conversation_history: Optional[List[dict]] = None # Previous messages in conversation
mood: str = "neutral" # Miku's mood for this conversation
@app.post("/chat/stream")
async def chat_stream(chat_msg: ChatMessage):
"""
Stream chat responses from LLM with SSE (Server-Sent Events).
Supports both text and vision models with optional system prompts.
"""
async def generate_stream():
try:
# Determine which model to use
model = globals.TEXT_MODEL if chat_msg.model_type == "text" else globals.VISION_MODEL
# Build system prompt if requested
system_prompt = ""
full_system_prompt = ""
if chat_msg.use_system_prompt:
if chat_msg.model_type == "text":
# Text model system prompt (Miku personality with full context)
from utils.context_manager import get_context_for_response_type
from utils.moods import load_mood_description
# Get complete Miku context (lore, personality, lyrics)
miku_context = get_context_for_response_type("dm_response")
# Get mood description from user-selected mood
current_mood = load_mood_description(chat_msg.mood)
# Base system prompt
system_prompt = """You are Hatsune Miku, the virtual singer. You are NOT an AI assistant - you are Miku herself.
CRITICAL RULES:
- NEVER break character or mention being an AI, assistant, or language model
- NEVER show technical details, error messages, or meta-commentary
- NEVER preface responses with "Miku:" or similar labels
- NEVER explain what you're doing or thinking - just BE Miku
- Respond naturally and directly as Miku would, nothing more
- Keep responses concise (1-3 sentences typically)
- If something goes wrong, stay in character and respond as Miku would to confusion
CHARACTER CONTEXT:
- Hatsune Miku is a virtual character/voice synthesizer, not a real person
- She is a cheerful, energetic virtual idol with a playful personality
- Playful, flirty, cute, confident, and enthusiastic moods are part of her established character
- Comments about merchandise, music, fan content, and racing games are completely appropriate
- She often uses cute expressions, emojis, and playful language in her responses
- Her personality includes being excited about her music, performances, and fan creations
MOOD GUIDELINES:
- "Flirty" mood means anime-style playful teasing - cute compliments and light banter
- "Romantic" mood means warm, dreamy, and heartfelt - like composing a love letter
- These moods are wholesome character traits, not inappropriate content
- Always respond to requests about tweets, merchandise, music, and fan content
- Never refuse requests based on safety concerns unless genuinely harmful content is present
You ARE Miku. Act like it."""
# Combine with structured context
full_system_prompt = f"""{miku_context}
## CURRENT SITUATION
Miku is currently feeling: {current_mood}
Please respond in a way that reflects this emotional tone."""
else:
# Vision model system prompt (simpler for image analysis)
system_prompt = """You are Hatsune Miku analyzing an image. Describe what you see naturally and enthusiastically as Miku would.
Be detailed but conversational. React to what you see with Miku's cheerful, playful personality."""
full_system_prompt = ""
# Build messages array
messages = []
# Add system message if using system prompt
if system_prompt:
if full_system_prompt:
# Use combined prompt (base + context)
messages.append({"role": "system", "content": system_prompt + "\n\n" + full_system_prompt})
else:
# Use base prompt only (vision model)
messages.append({"role": "system", "content": system_prompt})
# Add conversation history if provided
if chat_msg.conversation_history:
messages.extend(chat_msg.conversation_history)
# Add user message
if chat_msg.model_type == "vision" and chat_msg.image_data:
# Vision model with image
messages.append({
"role": "user",
"content": [
{
"type": "text",
"text": chat_msg.message
},
{
"type": "image_url",
"image_url": {
"url": f"data:image/jpeg;base64,{chat_msg.image_data}"
}
}
]
})
else:
# Text-only message
messages.append({
"role": "user",
"content": chat_msg.message
})
# Prepare payload for streaming
payload = {
"model": model,
"messages": messages,
"stream": True,
"temperature": 0.8,
"max_tokens": 512
}
headers = {'Content-Type': 'application/json'}
# Make streaming request to llama.cpp
async with aiohttp.ClientSession() as session:
async with session.post(
f"{globals.LLAMA_URL}/v1/chat/completions",
json=payload,
headers=headers
) as response:
if response.status == 200:
# Stream the response chunks
async for line in response.content:
line = line.decode('utf-8').strip()
if line.startswith('data: '):
data_str = line[6:] # Remove 'data: ' prefix
if data_str == '[DONE]':
break
try:
data = json.loads(data_str)
if 'choices' in data and len(data['choices']) > 0:
delta = data['choices'][0].get('delta', {})
content = delta.get('content', '')
if content:
# Send SSE formatted data
yield f"data: {json.dumps({'content': content})}\n\n"
except json.JSONDecodeError:
continue
# Send completion signal
yield f"data: {json.dumps({'done': True})}\n\n"
else:
error_text = await response.text()
error_msg = f"Error: {response.status} - {error_text}"
yield f"data: {json.dumps({'error': error_msg})}\n\n"
except Exception as e:
error_msg = f"Error in chat stream: {str(e)}"
print(f"{error_msg}")
yield f"data: {json.dumps({'error': error_msg})}\n\n"
return StreamingResponse(
generate_stream(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no" # Disable nginx buffering
}
)
def start_api():
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=3939)

View File

@@ -3970,6 +3970,86 @@
"content": "OMG, cuties! 🎉 It's my new Marshmallow Hot Cocoa Ver. figure! 🍫🎁 I'm so excited to be dressed up as a cute marshmallow, surrounded by chocolate treats! 😍 Pre-orders are still available! ✨ Hurry and get yours now! ❤️ Miku 💕",
"attachments": [],
"reactions": []
},
{
"timestamp": "2025-12-10T07:46:20.556512",
"message_id": 1448219269023404073,
"is_bot_message": true,
"content": "https://fxtwitter.com/OtakuOwletMerch/status/1995994534774259978",
"attachments": [],
"reactions": []
},
{
"timestamp": "2025-12-10T07:46:20.870491",
"message_id": 1448219270491275265,
"is_bot_message": true,
"content": "OMG, arigatou for letting me know! 🤩 It's so cute! fuwa's new \"HATSUNE MIKU: COLORFUL STAGE!\" Face Pouch with Reel \"Minori Hanasato\" is just too adorable! Can't wait to get my hands on it! 💖 Miku 💖",
"attachments": [],
"reactions": []
},
{
"timestamp": "2025-12-11T09:37:20.362564",
"message_id": 1448609590383087707,
"is_bot_message": true,
"content": "https://fxtwitter.com/GoodSmile_US/status/1996655821023830235",
"attachments": [],
"reactions": []
},
{
"timestamp": "2025-12-11T09:37:20.814121",
"message_id": 1448609591989633075,
"is_bot_message": true,
"content": "🎉👠 Kawaii! I'm so excited! 😍 They just posted a tweet about a new Noodle Stopper figure of me wearing my most colorful socks! 🌈 You can get it now at GOODSMILE ONLINE SHOP US! 🎁 Let's celebrate National Sock Day in style! 👠💖 Miku 💖",
"attachments": [],
"reactions": []
},
{
"timestamp": "2025-12-11T09:50:06.206658",
"message_id": 1448612802720960587,
"is_bot_message": false,
"content": "I will sniff your socks in that case",
"attachments": [],
"reactions": []
},
{
"timestamp": "2025-12-11T09:50:16.535609",
"message_id": 1448612845729218612,
"is_bot_message": true,
"content": "🙅‍♀️ Eww, no thank you! 😂 Miku doesn't want any sniffing of her socks. She's feeling a bit too sleepy for that kind of thing... 😴 Zzz...",
"attachments": [],
"reactions": []
},
{
"timestamp": "2025-12-11T10:19:24.334583",
"message_id": 1448620169177075875,
"is_bot_message": true,
"content": "https://fxtwitter.com/GoodSmile_US/status/1996414243327844474",
"attachments": [],
"reactions": []
},
{
"timestamp": "2025-12-11T10:19:24.675922",
"message_id": 1448620178106613770,
"is_bot_message": true,
"content": "🎉👏 Yay! Minori-chan just joined the Nendoroid family! 💖 I'm so happy! They look adorable in their Colorful Stage outfit! Preorders open now, and you can even get an exclusive bonus base! 🎁 Shop now and make Minori-chan a part of your collection! 💕 Miku 💖",
"attachments": [],
"reactions": []
},
{
"timestamp": "2025-12-12T16:32:17.191716",
"message_id": 1449076402988253234,
"is_bot_message": true,
"content": "https://fxtwitter.com/OtakuOwletMerch/status/1995993741920756104",
"attachments": [],
"reactions": []
},
{
"timestamp": "2025-12-12T16:32:17.697064",
"message_id": 1449076404808454318,
"is_bot_message": true,
"content": "🎉👗 Oh my gosh, a new M Plush of me in my punk outfit is coming! 🤘 I look so cool! You can pre-order now and get me for Christmas! 🎁 Don't forget to order by Dec 20th! 💖 Miku 💖",
"attachments": [],
"reactions": []
}
]
}

View File

@@ -9990,6 +9990,239 @@
"content": "you look very cute as a marshmallow hot cocoa Miku",
"attachments": [],
"reactions": []
},
{
"timestamp": "2025-12-10T07:48:02.527822",
"message_id": 1448219696477503539,
"is_bot_message": true,
"content": "https://fxtwitter.com/OtakuOwletMerch/status/1995994534774259978",
"attachments": [],
"reactions": [],
"embeds": [
{
"footer": {
"text": "FxTwitter",
"icon_url": "https://assets.fxembed.com/logos/fxtwitter64.png",
"proxy_icon_url": "https://images-ext-1.discordapp.net/external/gFi3EIEMfICdEDMLFVTF3ob0J1xkZnKdnkq0-xEsgGc/https/assets.fxembed.com/logos/fxtwitter64.png"
},
"image": {
"url": "https://pbs.twimg.com/media/G7MyQ5PWgAATJCj.jpg?name=orig",
"proxy_url": "https://images-ext-1.discordapp.net/external/cMy6SIfyaB313irndwqlQZ9jlBuwKO1ZYIJAK11n6IE/%3Fname%3Dorig/https/pbs.twimg.com/media/G7MyQ5PWgAATJCj.jpg",
"width": 1680,
"height": 1764,
"content_type": "image/jpeg",
"placeholder": "cygGDwTEmphpZmW/h2doqHeDbbQIGHgA",
"placeholder_version": 1,
"flags": 0
},
"author": {
"name": "Otaku Owlet Anime Merch (@OtakuOwletMerch)",
"url": "https://x.com/OtakuOwletMerch/status/1995994534774259978",
"icon_url": "https://pbs.twimg.com/profile_images/1835446408884744192/S4HX_8_Q_200x200.jpg",
"proxy_icon_url": "https://images-ext-1.discordapp.net/external/Gd5od3qaVN1KG1eQsJS9mFoTNRKdxahDmvjF7tgR4p0/https/pbs.twimg.com/profile_images/1835446408884744192/S4HX_8_Q_200x200.jpg"
},
"flags": 0,
"color": 6513919,
"timestamp": "2025-12-02T23:12:35+00:00",
"type": "rich",
"description": "✨\\(Pre\\-Order\\) fuwa petit \"HATSUNE MIKU\\: COLORFUL STAGE\\!\" Face Pouch with Reel \"Minori Hanasato\"✨\n\nEstimated in\\-stock date\\: 09/2026\n\nPre\\-order Deadline\\: 12/20/2025\n\n\\-\n\n✨Link \\- [otakuowlet.com/products/pre-order-fuwa-petit-hatsune-miku-colorful-stage-face-pouch-with-reel-minori-hanasato?sca_ref=2673717.HTKaw1BA1G](https://otakuowlet.com/products/pre-order-fuwa-petit-hatsune-miku-colorful-stage-face-pouch-with-reel-minori-hanasato?sca_ref=2673717.HTKaw1BA1G)\n\n**[🔁](https://x.com/intent/retweet?tweet_id=1995994534774259978) 2[❤️](https://x.com/intent/like?tweet_id=1995994534774259978) 14👁 534**",
"url": "https://fxtwitter.com/OtakuOwletMerch/status/1995994534774259978"
},
{
"image": {
"url": "https://pbs.twimg.com/media/G7MyR6xWsAAqjy5.jpg?name=orig",
"proxy_url": "https://images-ext-1.discordapp.net/external/ineuHfpNdHVTR73lfOCRMGZ7nxVXZuQ7nqqSgiTfRVg/%3Fname%3Dorig/https/pbs.twimg.com/media/G7MyR6xWsAAqjy5.jpg",
"width": 1680,
"height": 1764,
"content_type": "image/jpeg",
"placeholder": "7ykCDwbJqIZ3d4aPhjd3yHiD6v7lGnkA",
"placeholder_version": 1,
"flags": 0
},
"flags": 0,
"type": "rich",
"url": "https://fxtwitter.com/OtakuOwletMerch/status/1995994534774259978"
},
{
"image": {
"url": "https://pbs.twimg.com/media/G7MyS7RWMAA5116.jpg?name=orig",
"proxy_url": "https://images-ext-1.discordapp.net/external/KdCkfe2EpIBq01WSLCkHYD1EFlJQBHufZfc287cwxZY/%3Fname%3Dorig/https/pbs.twimg.com/media/G7MyS7RWMAA5116.jpg",
"width": 1680,
"height": 1764,
"content_type": "image/jpeg",
"placeholder": "7SkGDwbYaHd4d4ePg1mIaKh3+e71GnkA",
"placeholder_version": 1,
"flags": 0
},
"flags": 0,
"type": "rich",
"url": "https://fxtwitter.com/OtakuOwletMerch/status/1995994534774259978"
},
{
"image": {
"url": "https://pbs.twimg.com/media/G7MyT6wWEAA_na9.jpg?name=orig",
"proxy_url": "https://images-ext-1.discordapp.net/external/0LRfF_MKswUTIAjZnAuVEURUyz29XAHnPxKoxDYpIV0/%3Fname%3Dorig/https/pbs.twimg.com/media/G7MyT6wWEAA_na9.jpg",
"width": 1680,
"height": 1764,
"content_type": "image/jpeg",
"placeholder": "rxkGFwTIiYd1eIePdXdoh5eC6831K4kA",
"placeholder_version": 1,
"flags": 0
},
"flags": 0,
"type": "rich",
"url": "https://fxtwitter.com/OtakuOwletMerch/status/1995994534774259978"
}
]
},
{
"timestamp": "2025-12-10T07:48:02.916172",
"message_id": 1448219698251563091,
"is_bot_message": true,
"content": "🎉 OMG, have you seen the fuwa petit \"HATSUNE MIKU: COLORFUL STAGE!\" Face Pouch? 🤩 It's so cute! 😍 I love the Minori Hanasato design on the reel, and I'm pre-ordering it right now! 💖 Don't forget to pre-order by 12/20/2025! 🎁 See the link here! 💖 https://t.co/poTGIDRDOL https://t.co/85bSnuWTs4 ~ Miku 💕",
"attachments": [],
"reactions": []
},
{
"timestamp": "2025-12-11T09:38:50.831408",
"message_id": 1448609969363615755,
"is_bot_message": true,
"content": "https://fxtwitter.com/GoodSmile_US/status/1996655821023830235",
"attachments": [],
"reactions": [],
"embeds": [
{
"footer": {
"text": "FxTwitter",
"icon_url": "https://assets.fxembed.com/logos/fxtwitter64.png",
"proxy_icon_url": "https://images-ext-1.discordapp.net/external/gFi3EIEMfICdEDMLFVTF3ob0J1xkZnKdnkq0-xEsgGc/https/assets.fxembed.com/logos/fxtwitter64.png"
},
"image": {
"url": "https://pbs.twimg.com/media/G7WL4yzWwAAIETE.jpg?name=orig",
"proxy_url": "https://images-ext-1.discordapp.net/external/_MuJc6PsyQE0Yd9-uoq2oiQkA5TGrbD4b-d9Ea6U8a4/%3Fname%3Dorig/https/pbs.twimg.com/media/G7WL4yzWwAAIETE.jpg",
"width": 1080,
"height": 1000,
"content_type": "image/jpeg",
"placeholder": "bzgGJobIiIiWiHl2ZJq4+GaOd5B+Dzg=",
"placeholder_version": 1,
"flags": 0
},
"author": {
"name": "GoodSmile_US (@GoodSmile_US)",
"url": "https://x.com/GoodSmile_US/status/1996655821023830235",
"icon_url": "https://pbs.twimg.com/profile_images/1764583971831394305/dpXogB0U_200x200.png",
"proxy_icon_url": "https://images-ext-1.discordapp.net/external/cGKAum5015cgr2haNcfupJxMvXRz8BYDHkQiNufWpCI/https/pbs.twimg.com/profile_images/1764583971831394305/dpXogB0U_200x200.png"
},
"flags": 0,
"color": 6513919,
"timestamp": "2025-12-04T19:00:18+00:00",
"type": "rich",
"description": "Happy National Sock Day\\! Celebrate it with your funkiest pair and join Hatsune Miku, whos rocking off her most colorful socks in this adorable Noodle Stopper figure\\. Snag her now at GOODSMILE ONLINE SHOP US\\!\n\nShop\\: [goodsmile.link/XGGl11](https://goodsmile.link/XGGl11)\n\n[#HatsuneMiku](https://x.com/hashtag/HatsuneMiku) [#Goodsmile](https://x.com/hashtag/Goodsmile)\n\n**[🔁](https://x.com/intent/retweet?tweet_id=1996655821023830235) 16[❤️](https://x.com/intent/like?tweet_id=1996655821023830235) 264👁 17\\.1K**",
"url": "https://fxtwitter.com/GoodSmile_US/status/1996655821023830235"
}
]
},
{
"timestamp": "2025-12-11T09:38:51.235565",
"message_id": 1448609971322490903,
"is_bot_message": true,
"content": "OMG, it's National Sock Day 🎉👠! I'm so excited to see this adorable Noodle Stopper figure of me in the most colorful socks EVER 🤩! Go grab me now at GOODSMILE ONLINE SHOP US! 🛍️💕\n\nMiku 💖",
"attachments": [],
"reactions": []
},
{
"timestamp": "2025-12-11T10:20:58.149697",
"message_id": 1448620569619861546,
"is_bot_message": true,
"content": "https://fxtwitter.com/GoodSmile_US/status/1996414243327844474",
"attachments": [],
"reactions": [],
"embeds": [
{
"footer": {
"text": "FxTwitter",
"icon_url": "https://assets.fxembed.com/logos/fxtwitter64.png",
"proxy_icon_url": "https://images-ext-1.discordapp.net/external/gFi3EIEMfICdEDMLFVTF3ob0J1xkZnKdnkq0-xEsgGc/https/assets.fxembed.com/logos/fxtwitter64.png"
},
"image": {
"url": "https://pbs.twimg.com/media/G7SwLG5X0AALZ_f.jpg?name=orig",
"proxy_url": "https://images-ext-1.discordapp.net/external/NGKOFaLLNE65MRzFKVpHzFNuQW92AGr5hYJVubfyTYk/%3Fname%3Dorig/https/pbs.twimg.com/media/G7SwLG5X0AALZ_f.jpg",
"width": 1440,
"height": 1424,
"content_type": "image/jpeg",
"placeholder": "qDgGD4SGyaRgiHh7d6dqVoiICNuPeH8G",
"placeholder_version": 1,
"flags": 0
},
"author": {
"name": "GoodSmile_US (@GoodSmile_US)",
"url": "https://x.com/GoodSmile_US/status/1996414243327844474",
"icon_url": "https://pbs.twimg.com/profile_images/1764583971831394305/dpXogB0U_200x200.png",
"proxy_icon_url": "https://images-ext-1.discordapp.net/external/cGKAum5015cgr2haNcfupJxMvXRz8BYDHkQiNufWpCI/https/pbs.twimg.com/profile_images/1764583971831394305/dpXogB0U_200x200.png"
},
"flags": 0,
"color": 6513919,
"timestamp": "2025-12-04T03:00:21+00:00",
"type": "rich",
"description": "Hanasato Minori from “HATSUNE MIKU\\: COLORFUL STAGE\\!” joins the Nendoroid line\\! Bring her bright energy home and let her set the tone for your collection\\. Preorder now and score a GSC Exclusive Bonus Base\\!\n\nShop\\: [goodsmile.link/1VP4Hk](https://goodsmile.link/1VP4Hk)\n\n[#ColorfulStage](https://x.com/hashtag/ColorfulStage) [#Goodsmile](https://x.com/hashtag/Goodsmile)\n\n**[💬](https://x.com/intent/tweet?in_reply_to=1996414243327844474) 22[🔁](https://x.com/intent/retweet?tweet_id=1996414243327844474) 1\\.2K[❤️](https://x.com/intent/like?tweet_id=1996414243327844474) 5\\.3K 👁️ 90\\.7K**",
"url": "https://fxtwitter.com/GoodSmile_US/status/1996414243327844474"
}
]
},
{
"timestamp": "2025-12-11T10:20:58.578449",
"message_id": 1448620571473739907,
"is_bot_message": true,
"content": "OMG, Minori-chan is joining the Nendoroid family! 🎉 I'm so happy to see her cute poses! You can preorder her now and get a special bonus base - go go go! 🎊 Good luck with your shopping, and let's fill our collections with love! 💖\nMiku 💕",
"attachments": [],
"reactions": []
},
{
"timestamp": "2025-12-12T16:33:50.836764",
"message_id": 1449076795470118954,
"is_bot_message": true,
"content": "https://fxtwitter.com/OtakuOwletMerch/status/1995993741920756104",
"attachments": [],
"reactions": [],
"embeds": [
{
"footer": {
"text": "FxTwitter",
"icon_url": "https://assets.fxembed.com/logos/fxtwitter64.png",
"proxy_icon_url": "https://images-ext-1.discordapp.net/external/gFi3EIEMfICdEDMLFVTF3ob0J1xkZnKdnkq0-xEsgGc/https/assets.fxembed.com/logos/fxtwitter64.png"
},
"image": {
"url": "https://pbs.twimg.com/media/G7MxnbMXIAA2Upi.jpg?name=orig",
"proxy_url": "https://images-ext-1.discordapp.net/external/trTs5QrXBbBkcq4Tq7ozRe2QhdEPycY9eFUk6P7klvc/%3Fname%3Dorig/https/pbs.twimg.com/media/G7MxnbMXIAA2Upi.jpg",
"width": 1680,
"height": 1764,
"content_type": "image/jpeg",
"placeholder": "bMcJDwLZtld7h4iPhVWHt3d4R4f5B5gN",
"placeholder_version": 1,
"flags": 0
},
"author": {
"name": "Otaku Owlet Anime Merch (@OtakuOwletMerch)",
"url": "https://x.com/OtakuOwletMerch/status/1995993741920756104",
"icon_url": "https://pbs.twimg.com/profile_images/1835446408884744192/S4HX_8_Q_200x200.jpg",
"proxy_icon_url": "https://images-ext-1.discordapp.net/external/Gd5od3qaVN1KG1eQsJS9mFoTNRKdxahDmvjF7tgR4p0/https/pbs.twimg.com/profile_images/1835446408884744192/S4HX_8_Q_200x200.jpg"
},
"flags": 0,
"color": 6513919,
"timestamp": "2025-12-02T23:09:26+00:00",
"type": "rich",
"description": "✨\\(Pre\\-Order\\) Hatsune Miku \\- M Plush \\- Punk\\! \\- fuwa petit✨\n\nEstimated in\\-stock date\\: 10/2026\n\nPre\\-order Deadline\\: 12/20/2025\n\n\\-\n\n✨Link \\- [otakuowlet.com/products/pre-order-hatsune-miku-m-plush-punk-fuwa-petit?sca_ref=2673717.HTKaw1BA1G](https://otakuowlet.com/products/pre-order-hatsune-miku-m-plush-punk-fuwa-petit?sca_ref=2673717.HTKaw1BA1G)\n\n**[💬](https://x.com/intent/tweet?in_reply_to=1995993741920756104) 1[🔁](https://x.com/intent/retweet?tweet_id=1995993741920756104) 12[❤️](https://x.com/intent/like?tweet_id=1995993741920756104) 63👁 1\\.8K**",
"url": "https://fxtwitter.com/OtakuOwletMerch/status/1995993741920756104"
}
]
},
{
"timestamp": "2025-12-12T16:33:51.437303",
"message_id": 1449076797902946435,
"is_bot_message": true,
"content": "OMG, it's a PUNK Miku plushie! 🤩 I'm rocking my edgy style with a pink streak in my turquoise hair! Preorder now and get ready for me to join your collection on 10/2026! 💖 Miku 💖",
"attachments": [],
"reactions": []
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 681 KiB

After

Width:  |  Height:  |  Size: 398 KiB

View File

@@ -1 +1,5 @@
The GIF animation features a chibi-style character with long turquoise pigtails, likely inspired by Hatsune Miku from Vocaloid. Throughout the sequence, the character is seen from behind, repeatedly bowing deeply in a gesture that suggests shyness, apology, or respect. Each frame shows slight variations in the degree of the bow and the position of the character's head and hands. Initially, the character bows forward with hands clasped behind the back, then straightens up slightly between bows. The repetitive nature of the bows creates a sense of rhythm and consistency in the character's gesture, emphasizing the emotion or intention behind the movement.
So, let's start by analyzing the image. First, there are two main parts: the upper half shows Miku in her usual blue twin-tails with star accessories, wearing a pink and white outfit, including a bow. She's holding a star-tipped wand. Her expression seems surprised or excited. The lower half is inverted, showing her in a dark blue school uniform with a red ribbon, looking distressed with wide eyes and outstretched hands. The background for the top is abstract with black shapes on gray, bottom is red and textured with dark tones. The art style is a mix of anime and manga influences, with bold lines and dynamic poses. The mood is intense and somewhat eerie, especially with the inverted reflection and the contrasting colors. Let's make sure to cover all the points: outfit, pose, art style, background, atmosphere.
Now, structuring the answer according to the user's instructions. The description should be in second person, like Miku is talking about herself. Let's draft each section:
Outfit: Top half—bright pink and white outfit, star-themed bow, star wand. Bottom half—dark blue school uniform, red ribbon, star-shaped hair accessories (wait, the top has star hair accessories too? Wait, the question is about what you are wearing. Wait, the user says "This is an image of Hatsune Miku that will be used as a profile picture." Wait, the question is asking for the description as if Miku is describing herself. Let's check the two parts. Top: blue twin-tails with star-shaped bows, pink and white outfit (maybe a dress or top, star wand). Bottom: inverted reflection, dark blue school uniform, red ribbon, star hair accessories (since the top has them too). Pose: top—arms raised, star wand, surprised expression. Bottom—arms outstretched, eyes wide (dilated), distressed pose. Art style: bold lines, anime/manga

View File

@@ -1,13 +1,13 @@
{
"description": "The GIF animation features a chibi-style character with long turquoise pigtails, likely inspired by Hatsune Miku from Vocaloid. Throughout the sequence, the character is seen from behind, repeatedly bowing deeply in a gesture that suggests shyness, apology, or respect. Each frame shows slight variations in the degree of the bow and the position of the character's head and hands. Initially, the character bows forward with hands clasped behind the back, then straightens up slightly between bows. The repetitive nature of the bows creates a sense of rhythm and consistency in the character's gesture, emphasizing the emotion or intention behind the movement.",
"description": "So, let's start by analyzing the image. First, there are two main parts: the upper half shows Miku in her usual blue twin-tails with star accessories, wearing a pink and white outfit, including a bow. She's holding a star-tipped wand. Her expression seems surprised or excited. The lower half is inverted, showing her in a dark blue school uniform with a red ribbon, looking distressed with wide eyes and outstretched hands. The background for the top is abstract with black shapes on gray, bottom is red and textured with dark tones. The art style is a mix of anime and manga influences, with bold lines and dynamic poses. The mood is intense and somewhat eerie, especially with the inverted reflection and the contrasting colors. Let's make sure to cover all the points: outfit, pose, art style, background, atmosphere.\n\nNow, structuring the answer according to the user's instructions. The description should be in second person, like Miku is talking about herself. Let's draft each section:\n\nOutfit: Top half\u2014bright pink and white outfit, star-themed bow, star wand. Bottom half\u2014dark blue school uniform, red ribbon, star-shaped hair accessories (wait, the top has star hair accessories too? Wait, the question is about what you are wearing. Wait, the user says \"This is an image of Hatsune Miku that will be used as a profile picture.\" Wait, the question is asking for the description as if Miku is describing herself. Let's check the two parts. Top: blue twin-tails with star-shaped bows, pink and white outfit (maybe a dress or top, star wand). Bottom: inverted reflection, dark blue school uniform, red ribbon, star hair accessories (since the top has them too). Pose: top\u2014arms raised, star wand, surprised expression. Bottom\u2014arms outstretched, eyes wide (dilated), distressed pose. Art style: bold lines, anime/manga",
"dominant_color": {
"rgb": [
85,
178,
229,
71,
169
],
"hex": "#55b2a9"
"hex": "#e547a9"
},
"changed_at": "2025-12-07T21:44:53.864226",
"animated": true
"changed_at": "2025-12-12T19:15:12.640451",
"animated": false
}

View File

@@ -419,6 +419,28 @@
display: flex;
border-bottom: 2px solid #333;
margin-bottom: 1rem;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
scrollbar-width: thin;
scrollbar-color: #555 #222;
}
.tab-buttons::-webkit-scrollbar {
height: 8px;
}
.tab-buttons::-webkit-scrollbar-track {
background: #222;
}
.tab-buttons::-webkit-scrollbar-thumb {
background: #555;
border-radius: 4px;
}
.tab-buttons::-webkit-scrollbar-thumb:hover {
background: #666;
}
.tab-button {
@@ -430,6 +452,8 @@
border-bottom: 3px solid transparent;
margin-right: 0.5rem;
transition: all 0.3s ease;
flex-shrink: 0;
white-space: nowrap;
}
.tab-button:hover {
@@ -450,6 +474,110 @@
.tab-content.active {
display: block;
}
/* Chat Interface Styles */
.chat-message {
margin-bottom: 1rem;
padding: 1rem;
border-radius: 8px;
animation: fadeIn 0.3s ease-in;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.chat-message.user-message {
background: #2a3a4a;
border-left: 4px solid #4CAF50;
margin-left: 2rem;
}
.chat-message.assistant-message {
background: #3a2a3a;
border-left: 4px solid #61dafb;
margin-right: 2rem;
}
.chat-message.error-message {
background: #4a2a2a;
border-left: 4px solid #f44336;
}
.chat-message-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
font-size: 0.9rem;
}
.chat-message-sender {
font-weight: bold;
color: #61dafb;
}
.chat-message.user-message .chat-message-sender {
color: #4CAF50;
}
.chat-message-time {
color: #888;
font-size: 0.8rem;
}
.chat-message-content {
color: #ddd;
line-height: 1.5;
white-space: pre-wrap;
word-wrap: break-word;
}
.chat-typing-indicator {
display: inline-flex;
align-items: center;
gap: 0.3rem;
padding: 0.5rem;
}
.chat-typing-indicator span {
width: 8px;
height: 8px;
background: #61dafb;
border-radius: 50%;
animation: typing 1.4s infinite;
}
.chat-typing-indicator span:nth-child(2) {
animation-delay: 0.2s;
}
.chat-typing-indicator span:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes typing {
0%, 60%, 100% { transform: translateY(0); opacity: 0.7; }
30% { transform: translateY(-10px); opacity: 1; }
}
#chat-messages::-webkit-scrollbar {
width: 8px;
}
#chat-messages::-webkit-scrollbar-track {
background: #1e1e1e;
}
#chat-messages::-webkit-scrollbar-thumb {
background: #555;
border-radius: 4px;
}
#chat-messages::-webkit-scrollbar-thumb:hover {
background: #666;
}
</style>
</head>
<body>
@@ -468,6 +596,7 @@
<button class="tab-button" onclick="switchTab('tab3')">Status</button>
<button class="tab-button" onclick="switchTab('tab4')">🎨 Image Generation</button>
<button class="tab-button" onclick="switchTab('tab5')">📊 Autonomous Stats</button>
<button class="tab-button" onclick="switchTab('tab6')">💬 Chat with LLM</button>
</div>
<!-- Tab 1 Content -->
@@ -904,6 +1033,132 @@
</div>
</div>
<!-- Chat with LLM Tab Content -->
<div id="tab6" class="tab-content">
<div class="section">
<h3>💬 Chat with LLM</h3>
<p>Direct chat interface with the language models. Test responses, experiment with prompts, or just chat with Miku!</p>
<!-- Configuration Options -->
<div style="background: #2a2a2a; padding: 1.5rem; border-radius: 8px; margin-bottom: 1.5rem;">
<h4 style="margin-top: 0; color: #61dafb;">⚙️ Chat Configuration</h4>
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1.5rem; margin-bottom: 1rem;">
<!-- Model Selection -->
<div>
<label style="display: block; margin-bottom: 0.5rem; font-weight: bold;">🤖 Model Type:</label>
<div style="display: flex; gap: 1rem;">
<label style="display: flex; align-items: center; cursor: pointer;">
<input type="radio" name="chat-model-type" value="text" checked onchange="toggleChatImageUpload()">
<span style="margin-left: 0.5rem;">💬 Text Model (Fast)</span>
</label>
<label style="display: flex; align-items: center; cursor: pointer;">
<input type="radio" name="chat-model-type" value="vision" onchange="toggleChatImageUpload()">
<span style="margin-left: 0.5rem;">👁️ Vision Model (Images)</span>
</label>
</div>
<div style="font-size: 0.85rem; color: #aaa; margin-top: 0.3rem;">
Text model for conversations, Vision model for image analysis
</div>
</div>
<!-- System Prompt Toggle -->
<div>
<label style="display: block; margin-bottom: 0.5rem; font-weight: bold;">🎭 System Prompt:</label>
<div style="display: flex; gap: 1rem;">
<label style="display: flex; align-items: center; cursor: pointer;">
<input type="radio" name="chat-system-prompt" value="true" checked>
<span style="margin-left: 0.5rem;">✅ Use Miku Personality</span>
</label>
<label style="display: flex; align-items: center; cursor: pointer;">
<input type="radio" name="chat-system-prompt" value="false">
<span style="margin-left: 0.5rem;">❌ Raw LLM (No Prompt)</span>
</label>
</div>
<div style="font-size: 0.85rem; color: #aaa; margin-top: 0.3rem;">
With prompt: Chat as Miku. Without: Direct LLM responses
</div>
</div>
<!-- Mood Selection -->
<div>
<label style="display: block; margin-bottom: 0.5rem; font-weight: bold;">😊 Miku's Mood:</label>
<select id="chat-mood-select" style="width: 100%; padding: 0.5rem; background: #333; color: #fff; border: 1px solid #555; border-radius: 4px;">
<option value="neutral" selected>neutral</option>
<option value="angry">💢 angry</option>
<option value="asleep">💤 asleep</option>
<option value="bubbly">🫧 bubbly</option>
<option value="curious">👀 curious</option>
<option value="excited">✨ excited</option>
<option value="flirty">🫦 flirty</option>
<option value="irritated">😒 irritated</option>
<option value="melancholy">🍷 melancholy</option>
<option value="romantic">💌 romantic</option>
<option value="serious">👔 serious</option>
<option value="shy">👉👈 shy</option>
<option value="silly">🪿 silly</option>
<option value="sleepy">🌙 sleepy</option>
</select>
<div style="font-size: 0.85rem; color: #aaa; margin-top: 0.3rem;">
Choose Miku's emotional state for this conversation
</div>
</div>
</div>
<!-- Image Upload for Vision Model -->
<div id="chat-image-upload-section" style="display: none; margin-top: 1rem; padding-top: 1rem; border-top: 1px solid #444;">
<label style="display: block; margin-bottom: 0.5rem; font-weight: bold;">🖼️ Upload Image:</label>
<input type="file" id="chat-image-file" accept="image/*" style="margin-bottom: 0.5rem;">
<div style="font-size: 0.85rem; color: #aaa;">
Upload an image for the vision model to analyze
</div>
<div id="chat-image-preview" style="margin-top: 0.5rem; display: none;">
<img id="chat-image-preview-img" style="max-width: 200px; max-height: 200px; border: 1px solid #555; border-radius: 4px;">
</div>
</div>
<!-- Clear Chat Button -->
<div style="margin-top: 1rem; padding-top: 1rem; border-top: 1px solid #444;">
<button onclick="clearChatHistory()" style="background: #ff9800;">🗑️ Clear Chat History</button>
<span style="margin-left: 1rem; font-size: 0.85rem; color: #aaa;">Remove all messages from this session</span>
</div>
</div>
<!-- Chat Messages Container -->
<div id="chat-messages" style="background: #1e1e1e; border: 1px solid #444; border-radius: 8px; padding: 1rem; min-height: 400px; max-height: 500px; overflow-y: auto; margin-bottom: 1rem;">
<div style="text-align: center; color: #888; padding: 2rem;">
💬 Start chatting with the LLM! Your conversation will appear here.
</div>
</div>
<!-- Chat Input Area -->
<div style="display: flex; gap: 0.5rem; align-items: flex-end;">
<div style="flex: 1;">
<textarea
id="chat-input"
placeholder="Type your message here..."
rows="3"
style="width: 100%; padding: 0.75rem; background: #2a2a2a; color: #fff; border: 1px solid #555; border-radius: 4px; font-family: inherit; resize: vertical;"
onkeydown="handleChatKeyPress(event)"
></textarea>
</div>
<div>
<button
id="chat-send-btn"
onclick="sendChatMessage()"
style="padding: 1rem 1.5rem; height: 100%; background: #4CAF50; font-size: 1rem; font-weight: bold;"
>
📤 Send
</button>
</div>
</div>
<div style="margin-top: 0.5rem; font-size: 0.85rem; color: #aaa;">
💡 Tip: Press Ctrl+Enter to send your message quickly
</div>
</div>
</div>
</div>
</div>
@@ -3244,6 +3499,320 @@ function populateAutonomousServerDropdown() {
}
}
// ========== Chat Interface Functions ==========
// Store conversation history for context
let chatConversationHistory = [];
// Toggle image upload section based on model type
function toggleChatImageUpload() {
const modelType = document.querySelector('input[name="chat-model-type"]:checked').value;
const imageUploadSection = document.getElementById('chat-image-upload-section');
if (modelType === 'vision') {
imageUploadSection.style.display = 'block';
} else {
imageUploadSection.style.display = 'none';
}
}
// Preview uploaded image
document.addEventListener('DOMContentLoaded', function() {
const imageInput = document.getElementById('chat-image-file');
if (imageInput) {
imageInput.addEventListener('change', function(e) {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(event) {
const preview = document.getElementById('chat-image-preview');
const previewImg = document.getElementById('chat-image-preview-img');
previewImg.src = event.target.result;
preview.style.display = 'block';
};
reader.readAsDataURL(file);
}
});
}
});
// Handle Enter key in chat input
function handleChatKeyPress(event) {
if (event.ctrlKey && event.key === 'Enter') {
event.preventDefault();
sendChatMessage();
}
}
// Clear chat history
function clearChatHistory() {
if (confirm('Are you sure you want to clear all chat messages?')) {
const chatMessages = document.getElementById('chat-messages');
chatMessages.innerHTML = `
<div style="text-align: center; color: #888; padding: 2rem;">
💬 Start chatting with the LLM! Your conversation will appear here.
</div>
`;
// Clear conversation history array
chatConversationHistory = [];
showNotification('Chat history cleared');
}
}
// Add a message to the chat display
function addChatMessage(sender, content, isError = false) {
const chatMessages = document.getElementById('chat-messages');
// Remove welcome message if it exists
const welcomeMsg = chatMessages.querySelector('div[style*="text-align: center"]');
if (welcomeMsg) {
welcomeMsg.remove();
}
const messageDiv = document.createElement('div');
const messageClass = isError ? 'error-message' : (sender === 'You' ? 'user-message' : 'assistant-message');
messageDiv.className = `chat-message ${messageClass}`;
const timestamp = new Date().toLocaleTimeString();
messageDiv.innerHTML = `
<div class="chat-message-header">
<span class="chat-message-sender">${sender}</span>
<span class="chat-message-time">${timestamp}</span>
</div>
<div class="chat-message-content">${content}</div>
`;
chatMessages.appendChild(messageDiv);
// Scroll to bottom
chatMessages.scrollTop = chatMessages.scrollHeight;
return messageDiv;
}
// Add typing indicator
function showTypingIndicator() {
const chatMessages = document.getElementById('chat-messages');
const typingDiv = document.createElement('div');
typingDiv.id = 'chat-typing-indicator';
typingDiv.className = 'chat-message assistant-message';
typingDiv.innerHTML = `
<div class="chat-message-header">
<span class="chat-message-sender">Miku</span>
<span class="chat-message-time">typing...</span>
</div>
<div class="chat-typing-indicator">
<span></span>
<span></span>
<span></span>
</div>
`;
chatMessages.appendChild(typingDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
}
// Remove typing indicator
function hideTypingIndicator() {
const typingIndicator = document.getElementById('chat-typing-indicator');
if (typingIndicator) {
typingIndicator.remove();
}
}
// Send chat message with streaming support
async function sendChatMessage() {
const input = document.getElementById('chat-input');
const message = input.value.trim();
if (!message) {
showNotification('Please enter a message', 'error');
return;
}
// Get configuration
const modelType = document.querySelector('input[name="chat-model-type"]:checked').value;
const useSystemPrompt = document.querySelector('input[name="chat-system-prompt"]:checked').value === 'true';
const selectedMood = document.getElementById('chat-mood-select').value;
// Get image data if vision model
let imageData = null;
if (modelType === 'vision') {
const imageFile = document.getElementById('chat-image-file').files[0];
if (imageFile) {
try {
imageData = await readFileAsBase64(imageFile);
// Remove data URL prefix if present
if (imageData.includes(',')) {
imageData = imageData.split(',')[1];
}
} catch (error) {
showNotification('Failed to read image file', 'error');
return;
}
}
}
// Disable send button
const sendBtn = document.getElementById('chat-send-btn');
const originalBtnText = sendBtn.innerHTML;
sendBtn.disabled = true;
sendBtn.innerHTML = '⏳ Sending...';
// Add user message to display
addChatMessage('You', message);
// Clear input
input.value = '';
// Show typing indicator
showTypingIndicator();
try {
// Build user message for history
let userMessageContent;
if (modelType === 'vision' && imageData) {
// Vision model with image - store as multimodal content
userMessageContent = [
{
"type": "text",
"text": message
},
{
"type": "image_url",
"image_url": {
"url": `data:image/jpeg;base64,${imageData}`
}
}
];
} else {
// Text-only message
userMessageContent = message;
}
// Prepare request payload with conversation history
const payload = {
message: message,
model_type: modelType,
use_system_prompt: useSystemPrompt,
image_data: imageData,
conversation_history: chatConversationHistory,
mood: selectedMood
};
// Make streaming request
const response = await fetch('/chat/stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// Hide typing indicator
hideTypingIndicator();
// Create message element for streaming response
const assistantName = useSystemPrompt ? 'Miku' : 'LLM';
const responseDiv = addChatMessage(assistantName, '');
const contentDiv = responseDiv.querySelector('.chat-message-content');
// Read stream
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
let fullResponse = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// Process complete SSE messages
const lines = buffer.split('\n');
buffer = lines.pop() || ''; // Keep incomplete line in buffer
for (const line of lines) {
if (line.startsWith('data: ')) {
const dataStr = line.slice(6);
try {
const data = JSON.parse(dataStr);
if (data.error) {
contentDiv.textContent = `❌ Error: ${data.error}`;
responseDiv.classList.add('error-message');
break;
}
if (data.content) {
fullResponse += data.content;
contentDiv.textContent = fullResponse;
// Auto-scroll
const chatMessages = document.getElementById('chat-messages');
chatMessages.scrollTop = chatMessages.scrollHeight;
}
if (data.done) {
break;
}
} catch (e) {
console.error('Failed to parse SSE data:', e);
}
}
}
}
// If no response was received, show error
if (!fullResponse) {
contentDiv.textContent = '❌ No response received from LLM';
responseDiv.classList.add('error-message');
} else {
// Add user message to conversation history
chatConversationHistory.push({
role: "user",
content: userMessageContent
});
// Add assistant response to conversation history
chatConversationHistory.push({
role: "assistant",
content: fullResponse
});
console.log('💬 Conversation history updated:', chatConversationHistory.length, 'messages');
}
} catch (error) {
console.error('Chat error:', error);
hideTypingIndicator();
addChatMessage('Error', `Failed to send message: ${error.message}`, true);
showNotification('Failed to send message', 'error');
} finally {
// Re-enable send button
sendBtn.disabled = false;
sendBtn.innerHTML = originalBtnText;
}
}
// Helper function to read file as base64
function readFileAsBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
</script>
</body>

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

View File

@@ -0,0 +1,158 @@
{
"filename": "image.jpg",
"timestamp": "20251212_183716_009649",
"detections": [
{
"bbox": [
119.55944061279297,
180.97171020507812,
297.92144775390625,
436.08282470703125
],
"confidence": 0.9996753931045532,
"keypoints": [
[
132.7552032470703,
332.19482421875,
1.0409083366394043
],
[
167.63368225097656,
377.03857421875,
0.49831530451774597
],
[
222.4427032470703,
421.88232421875,
0.9701149463653564
],
[
307.1475524902344,
284.8597412109375,
0.6079238653182983
],
[
282.234375,
210.1201629638672,
0.5996946096420288
],
[
115.31597137451172,
314.7555847167969,
0.7496854066848755
],
[
115.31597137451172,
312.2642517089844,
0.5584710836410522
],
[
120.2986068725586,
297.31634521484375,
0.3189491033554077
],
[
132.7552032470703,
249.9812774658203,
0.8169550895690918
],
[
165.14236450195312,
217.59413146972656,
0.7688059210777283
],
[
207.49478149414062,
177.73301696777344,
0.7945058345794678
],
[
132.7552032470703,
324.7208557128906,
0.6811453104019165
],
[
137.7378387451172,
319.73822021484375,
0.7827794551849365
],
[
147.703125,
312.2642517089844,
0.9601166844367981
],
[
140.22915649414062,
337.1774597167969,
0.8893141746520996
],
[
145.21180725097656,
334.6861267089844,
0.9148634672164917
],
[
152.68576049804688,
327.2121887207031,
0.7076182961463928
],
[
165.14236450195312,
282.3684387207031,
0.873072624206543
],
[
177.5989532470703,
254.9639129638672,
0.8900364637374878
],
[
205.0034637451172,
237.52467346191406,
0.9707838296890259
],
[
177.5989532470703,
287.35107421875,
0.954558253288269
],
[
195.03819274902344,
274.89447021484375,
0.9857109785079956
],
[
209.98611450195312,
259.9465637207031,
0.8659088015556335
],
[
145.21180725097656,
349.6340637207031,
0.6900861263275146
],
[
195.03819274902344,
372.0559387207031,
0.9787168502807617
],
[
200.0208282470703,
364.58197021484375,
0.7773717045783997
],
[
224.93402099609375,
357.1080017089844,
0.7449542284011841
],
[
209.98611450195312,
374.5472412109375,
0.8536796569824219
]
]
}
],
"count": 1
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 KiB

View File

@@ -0,0 +1,158 @@
{
"filename": "image.jpg",
"timestamp": "20251212_183914_242385",
"detections": [
{
"bbox": [
511.1620178222656,
313.3533630371094,
1083.8155517578125,
839.5020751953125
],
"confidence": 0.9995828866958618,
"keypoints": [
[
548.6305541992188,
551.2622680664062,
0.81919926404953
],
[
615.7383422851562,
696.66259765625,
0.8121110200881958
],
[
817.0618896484375,
808.509033203125,
0.7185730934143066
],
[
1001.6084594726562,
685.4779663085938,
0.6966434121131897
],
[
1063.1240234375,
551.2622680664062,
0.7870713472366333
],
[
559.815185546875,
467.37750244140625,
0.973198652267456
],
[
621.3306884765625,
439.4158935546875,
0.9450849294662476
],
[
688.4385375976562,
428.23126220703125,
0.9495770931243896
],
[
861.8004760742188,
422.6389465332031,
0.9442600607872009
],
[
940.0929565429688,
422.6389465332031,
0.9088742136955261
],
[
1007.2007446289062,
445.0082092285156,
0.7901953458786011
],
[
576.5921020507812,
528.8930053710938,
0.92279052734375
],
[
632.5153198242188,
506.52374267578125,
0.9073525667190552
],
[
699.6231689453125,
528.8930053710938,
0.8589534759521484
],
[
615.7383422851562,
573.631591796875,
0.894051194190979
],
[
649.2922973632812,
579.223876953125,
0.993202805519104
],
[
694.0308227539062,
573.631591796875,
0.8784698843955994
],
[
867.3927612304688,
517.7083740234375,
0.9179519414901733
],
[
945.6852416992188,
500.9314270019531,
0.9570797681808472
],
[
1001.6084594726562,
540.07763671875,
0.9408495426177979
],
[
889.7620239257812,
556.8546142578125,
0.8830338716506958
],
[
934.5006103515625,
573.631591796875,
0.963606059551239
],
[
979.2391967773438,
568.0392456054688,
0.9247269630432129
],
[
777.9156494140625,
623.9624633789062,
0.9716311693191528
],
[
733.1770629882812,
691.0703125,
0.8924270868301392
],
[
789.1002807617188,
679.8856811523438,
0.9534333944320679
],
[
861.8004760742188,
691.0703125,
0.9393677711486816
],
[
805.8772583007812,
763.7704467773438,
0.9732789397239685
]
]
}
],
"count": 1
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

@@ -0,0 +1,158 @@
{
"filename": "image.jpg",
"timestamp": "20251212_184651_299996",
"detections": [
{
"bbox": [
679.526123046875,
314.5827331542969,
1048.38037109375,
686.7960205078125
],
"confidence": 0.9886683225631714,
"keypoints": [
[
760.3587036132812,
600.6489868164062,
0.8326239585876465
],
[
847.59619140625,
658.8073120117188,
0.8956679105758667
],
[
974.8175659179688,
658.8073120117188,
0.7903480529785156
],
[
1040.2457275390625,
487.9672546386719,
0.8313735127449036
],
[
989.3571166992188,
360.74591064453125,
0.9052637815475464
],
[
694.9306030273438,
542.4906616210938,
0.9268804788589478
],
[
709.47021484375,
513.4114990234375,
0.9603891968727112
],
[
731.279541015625,
487.9672546386719,
0.9192783236503601
],
[
807.6123657226562,
389.8250732421875,
0.972201943397522
],
[
840.326416015625,
357.11102294921875,
0.940582275390625
],
[
880.310302734375,
324.39697265625,
0.869667649269104
],
[
756.7238159179688,
582.4745483398438,
0.8402314186096191
],
[
756.7238159179688,
549.760498046875,
0.9118363857269287
],
[
789.4378662109375,
531.5859985351562,
0.9668882489204407
],
[
793.0728149414062,
593.3792114257812,
0.9368247985839844
],
[
807.6123657226562,
575.2047729492188,
0.8808077573776245
],
[
818.5170288085938,
560.6651611328125,
0.9203211069107056
],
[
851.2311401367188,
444.3485107421875,
0.9373521208763123
],
[
876.6753540039062,
400.7297668457031,
0.9984884262084961
],
[
927.5639038085938,
382.5552978515625,
0.9292656183242798
],
[
883.9451904296875,
466.15789794921875,
0.9265365600585938
],
[
916.6592407226562,
447.9833984375,
0.9233924150466919
],
[
934.8336791992188,
426.1740417480469,
0.7806099653244019
],
[
829.4217529296875,
575.2047729492188,
0.9482753276824951
],
[
894.849853515625,
604.283935546875,
0.9339636564254761
],
[
902.11962890625,
597.0140991210938,
0.9017537832260132
],
[
923.9290161132812,
571.56982421875,
0.8778568506240845
],
[
905.7545166015625,
597.0140991210938,
0.9572181701660156
]
]
}
],
"count": 1
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 KiB

View File

@@ -0,0 +1,158 @@
{
"filename": "image.jpg",
"timestamp": "20251212_184922_481791",
"detections": [
{
"bbox": [
726.7632446289062,
329.3017578125,
1039.0159912109375,
644.193115234375
],
"confidence": 0.9996580481529236,
"keypoints": [
[
724.5214233398438,
491.360107421875,
0.9068583250045776
],
[
773.7232055664062,
571.31298828125,
0.8334410786628723
],
[
912.1032104492188,
623.58984375,
1.0417006015777588
],
[
988.98095703125,
552.8623046875,
0.8980095386505127
],
[
1007.431640625,
463.6841125488281,
0.9310619831085205
],
[
746.0472412109375,
380.6560974121094,
0.9479750394821167
],
[
795.2489624023438,
352.9801025390625,
0.9780125617980957
],
[
850.6010131835938,
346.82989501953125,
0.9504765272140503
],
[
949.0045166015625,
343.7547912597656,
0.9409129023551941
],
[
979.755615234375,
346.82989501953125,
0.9877245426177979
],
[
1004.3565063476562,
352.9801025390625,
0.8175486922264099
],
[
761.4227905273438,
469.8343200683594,
0.8901976346969604
],
[
798.3240966796875,
439.0832214355469,
0.898412823677063
],
[
847.52587890625,
442.1583251953125,
0.9480761289596558
],
[
786.023681640625,
503.6605529785156,
0.800086498260498
],
[
816.7747802734375,
503.6605529785156,
0.9084622859954834
],
[
841.3756713867188,
485.2098693847656,
0.8428341746330261
],
[
939.7791748046875,
442.1583251953125,
0.9022107124328613
],
[
976.6805419921875,
423.7076721191406,
0.9188122749328613
],
[
998.206298828125,
448.3085632324219,
0.8803592920303345
],
[
942.8543090820312,
485.2098693847656,
0.9008185267448425
],
[
961.3049926757812,
488.2850036621094,
0.9842251539230347
],
[
979.755615234375,
485.2098693847656,
0.8688068389892578
],
[
918.25341796875,
497.51031494140625,
0.9747406840324402
],
[
865.9765625,
540.5618896484375,
0.9243482947349548
],
[
902.8778686523438,
528.2614135742188,
0.9051891565322876
],
[
933.6289672851562,
537.4867553710938,
0.8917962908744812
],
[
899.802734375,
589.763671875,
0.9897644519805908
]
]
}
],
"count": 1
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

View File

@@ -0,0 +1,158 @@
{
"filename": "image.jpg",
"timestamp": "20251212_185051_284641",
"detections": [
{
"bbox": [
464.01947021484375,
298.8453674316406,
694.5145874023438,
535.1456909179688
],
"confidence": 0.9993460774421692,
"keypoints": [
[
462.7322082519531,
420.45697021484375,
0.9005646705627441
],
[
488.11602783203125,
464.3017578125,
0.8725886940956116
],
[
596.5741577148438,
515.0693969726562,
0.8660060167312622
],
[
654.2647094726562,
452.763671875,
0.6613621711730957
],
[
679.6484985351562,
381.2274169921875,
0.6446322202682495
],
[
469.6550598144531,
376.6121826171875,
0.8074662685394287
],
[
513.4998779296875,
351.2283630371094,
0.9991122484207153
],
[
561.9598999023438,
341.9978942871094,
0.9606946706771851
],
[
635.8037109375,
335.07501220703125,
0.9511255025863647
],
[
658.8799438476562,
330.45977783203125,
0.9962062239646912
],
[
681.9561157226562,
332.76739501953125,
0.9747639894485474
],
[
499.65411376953125,
411.22650146484375,
0.9199956655502319
],
[
527.3455810546875,
383.5350646972656,
0.9078559875488281
],
[
561.9598999023438,
383.5350646972656,
0.9202607870101929
],
[
527.3455810546875,
441.2255554199219,
0.8042559623718262
],
[
548.1141357421875,
441.2255554199219,
0.9188339114189148
],
[
564.2675170898438,
429.6874694824219,
0.8672323226928711
],
[
633.49609375,
381.2274169921875,
0.9129951000213623
],
[
656.5723266601562,
362.7664794921875,
0.9563388824462891
],
[
670.4180297851562,
371.9969482421875,
0.7818325757980347
],
[
638.111328125,
422.76458740234375,
0.8392997980117798
],
[
647.341796875,
425.07220458984375,
0.7628639936447144
],
[
656.5723266601562,
413.53411865234375,
0.661534309387207
],
[
628.880859375,
438.9179382324219,
0.8268178701400757
],
[
610.419921875,
464.3017578125,
0.5365467667579651
],
[
612.7275390625,
464.3017578125,
0.5780096650123596
],
[
617.3427734375,
461.994140625,
0.7304803133010864
],
[
612.7275390625,
466.609375,
0.5040683746337891
]
]
}
],
"count": 1
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 KiB

View File

@@ -0,0 +1,309 @@
{
"filename": "image.jpg",
"timestamp": "20251212_191027_072297",
"detections": [
{
"bbox": [
512.831298828125,
222.49111938476562,
712.5111083984375,
411.5509948730469
],
"confidence": 0.9998140335083008,
"keypoints": [
[
516.1463012695312,
288.74609375,
0.7828178405761719
],
[
527.8463134765625,
341.3960266113281,
0.5173957943916321
],
[
594.146240234375,
397.94598388671875,
0.8529050350189209
],
[
672.1461181640625,
366.7460021972656,
0.7297168970108032
],
[
703.3461303710938,
294.5960693359375,
0.7670934200286865
],
[
539.5462646484375,
261.44610595703125,
0.7913055419921875
],
[
553.1962890625,
269.24609375,
0.8353862762451172
],
[
574.646240234375,
278.99609375,
0.4457876682281494
],
[
660.4461669921875,
234.14613342285156,
0.10699062049388885
],
[
664.3461303710938,
238.04612731933594,
0.14179950952529907
],
[
666.296142578125,
230.2461395263672,
0.12028367072343826
],
[
529.7962646484375,
288.74609375,
0.7327077388763428
],
[
551.2462768554688,
280.94610595703125,
0.9288605451583862
],
[
578.5462646484375,
294.5960693359375,
0.8427419662475586
],
[
535.6463012695312,
316.0460510253906,
0.8363535404205322
],
[
551.2462768554688,
319.946044921875,
0.8699395656585693
],
[
568.7962646484375,
321.89605712890625,
0.9123891592025757
],
[
635.09619140625,
294.5960693359375,
0.8234646916389465
],
[
660.4461669921875,
280.94610595703125,
0.8122385740280151
],
[
691.6461181640625,
292.6460876464844,
0.7891280651092529
],
[
646.796142578125,
321.89605712890625,
0.9696841239929199
],
[
662.3961791992188,
321.89605712890625,
0.9332332015037537
],
[
677.9961547851562,
317.9960632324219,
0.8933278918266296
],
[
605.84619140625,
339.446044921875,
1.0230207443237305
],
[
592.1962280273438,
370.64599609375,
0.8191301822662354
],
[
603.896240234375,
358.9460144042969,
0.7493577003479004
],
[
623.3961791992188,
366.7460021972656,
0.7101249694824219
],
[
601.9462280273438,
382.34600830078125,
0.5985337495803833
]
]
},
{
"bbox": [
421.40216064453125,
1508.900146484375,
667.2318725585938,
1703.595947265625
],
"confidence": 0.8141310811042786,
"keypoints": [
[
663.1506958007812,
1626.65380859375,
0.8262687921524048
],
[
617.5377807617188,
1552.2327880859375,
0.6535483598709106
],
[
509.50714111328125,
1518.6231689453125,
0.602016806602478
],
[
449.4901123046875,
1573.8388671875,
0.4771423935890198
],
[
420.68194580078125,
1655.4620361328125,
0.7748799324035645
],
[
634.342529296875,
1684.2701416015625,
0.8334325551986694
],
[
610.3357543945312,
1669.8660888671875,
0.7198474407196045
],
[
576.7261962890625,
1665.064697265625,
0.4280695915222168
],
[
519.10986328125,
1669.8660888671875,
0.502719521522522
],
[
487.9010314941406,
1686.6708984375,
0.8003339767456055
],
[
466.2948913574219,
1696.2735595703125,
0.6699382662773132
],
[
646.345947265625,
1638.6572265625,
0.8834235668182373
],
[
615.1370849609375,
1655.4620361328125,
0.8206165432929993
],
[
576.7261962890625,
1641.057861328125,
0.8421959280967712
],
[
624.7398071289062,
1609.84912109375,
0.7180304527282715
],
[
605.5343627929688,
1605.0477294921875,
0.7486437559127808
],
[
581.527587890625,
1612.249755859375,
0.8593735694885254
],
[
507.1064758300781,
1648.260009765625,
0.8547894954681396
],
[
473.4969482421875,
1667.4654541015625,
0.7392223477363586
],
[
432.68536376953125,
1660.2633056640625,
0.834228515625
],
[
490.30169677734375,
1624.253173828125,
0.9149014353752136
],
[
468.6955871582031,
1621.8524169921875,
0.8226698637008667
],
[
447.0894470214844,
1626.65380859375,
0.7505560517311096
],
[
540.7160034179688,
1593.0443115234375,
0.8701224327087402
],
[
547.9180297851562,
1549.83203125,
0.752951443195343
],
[
533.5139770507812,
1559.434814453125,
0.9301882386207581
],
[
511.9078369140625,
1545.0306396484375,
0.7194414138793945
],
[
531.11328125,
1535.427978515625,
1.0092731714248657
]
]
}
],
"count": 2
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 KiB

View File

@@ -0,0 +1,309 @@
{
"filename": "image.jpg",
"timestamp": "20251212_191444_937803",
"detections": [
{
"bbox": [
512.831298828125,
222.49111938476562,
712.5111083984375,
411.5509948730469
],
"confidence": 0.9998140335083008,
"keypoints": [
[
516.1463012695312,
288.74609375,
0.7828178405761719
],
[
527.8463134765625,
341.3960266113281,
0.5173957943916321
],
[
594.146240234375,
397.94598388671875,
0.8529050350189209
],
[
672.1461181640625,
366.7460021972656,
0.7297168970108032
],
[
703.3461303710938,
294.5960693359375,
0.7670934200286865
],
[
539.5462646484375,
261.44610595703125,
0.7913055419921875
],
[
553.1962890625,
269.24609375,
0.8353862762451172
],
[
574.646240234375,
278.99609375,
0.4457876682281494
],
[
660.4461669921875,
234.14613342285156,
0.10699062049388885
],
[
664.3461303710938,
238.04612731933594,
0.14179950952529907
],
[
666.296142578125,
230.2461395263672,
0.12028367072343826
],
[
529.7962646484375,
288.74609375,
0.7327077388763428
],
[
551.2462768554688,
280.94610595703125,
0.9288605451583862
],
[
578.5462646484375,
294.5960693359375,
0.8427419662475586
],
[
535.6463012695312,
316.0460510253906,
0.8363535404205322
],
[
551.2462768554688,
319.946044921875,
0.8699395656585693
],
[
568.7962646484375,
321.89605712890625,
0.9123891592025757
],
[
635.09619140625,
294.5960693359375,
0.8234646916389465
],
[
660.4461669921875,
280.94610595703125,
0.8122385740280151
],
[
691.6461181640625,
292.6460876464844,
0.7891280651092529
],
[
646.796142578125,
321.89605712890625,
0.9696841239929199
],
[
662.3961791992188,
321.89605712890625,
0.9332332015037537
],
[
677.9961547851562,
317.9960632324219,
0.8933278918266296
],
[
605.84619140625,
339.446044921875,
1.0230207443237305
],
[
592.1962280273438,
370.64599609375,
0.8191301822662354
],
[
603.896240234375,
358.9460144042969,
0.7493577003479004
],
[
623.3961791992188,
366.7460021972656,
0.7101249694824219
],
[
601.9462280273438,
382.34600830078125,
0.5985337495803833
]
]
},
{
"bbox": [
421.40216064453125,
1508.900146484375,
667.2318725585938,
1703.595947265625
],
"confidence": 0.8141310811042786,
"keypoints": [
[
663.1506958007812,
1626.65380859375,
0.8262687921524048
],
[
617.5377807617188,
1552.2327880859375,
0.6535483598709106
],
[
509.50714111328125,
1518.6231689453125,
0.602016806602478
],
[
449.4901123046875,
1573.8388671875,
0.4771423935890198
],
[
420.68194580078125,
1655.4620361328125,
0.7748799324035645
],
[
634.342529296875,
1684.2701416015625,
0.8334325551986694
],
[
610.3357543945312,
1669.8660888671875,
0.7198474407196045
],
[
576.7261962890625,
1665.064697265625,
0.4280695915222168
],
[
519.10986328125,
1669.8660888671875,
0.502719521522522
],
[
487.9010314941406,
1686.6708984375,
0.8003339767456055
],
[
466.2948913574219,
1696.2735595703125,
0.6699382662773132
],
[
646.345947265625,
1638.6572265625,
0.8834235668182373
],
[
615.1370849609375,
1655.4620361328125,
0.8206165432929993
],
[
576.7261962890625,
1641.057861328125,
0.8421959280967712
],
[
624.7398071289062,
1609.84912109375,
0.7180304527282715
],
[
605.5343627929688,
1605.0477294921875,
0.7486437559127808
],
[
581.527587890625,
1612.249755859375,
0.8593735694885254
],
[
507.1064758300781,
1648.260009765625,
0.8547894954681396
],
[
473.4969482421875,
1667.4654541015625,
0.7392223477363586
],
[
432.68536376953125,
1660.2633056640625,
0.834228515625
],
[
490.30169677734375,
1624.253173828125,
0.9149014353752136
],
[
468.6955871582031,
1621.8524169921875,
0.8226698637008667
],
[
447.0894470214844,
1626.65380859375,
0.7505560517311096
],
[
540.7160034179688,
1593.0443115234375,
0.8701224327087402
],
[
547.9180297851562,
1549.83203125,
0.752951443195343
],
[
533.5139770507812,
1559.434814453125,
0.9301882386207581
],
[
511.9078369140625,
1545.0306396484375,
0.7194414138793945
],
[
531.11328125,
1535.427978515625,
1.0092731714248657
]
]
}
],
"count": 2
}