Implement chat and markdown views, enhance status bar, and add workflow panel
- Added `chat.rs` for rendering chat messages with timestamps and roles. - Introduced `markdown.rs` for rendering markdown content with styling. - Created `status.rs` to display the application status bar with session and message counts. - Developed `workflow.rs` to show the current workflow status, including tool calls and active jobs. - Established a `theme.rs` for centralized color management across the UI. - Updated `mod.rs` to include new modules and manage rendering logic.
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Role {
|
||||
#[serde(rename = "user")]
|
||||
User,
|
||||
#[serde(rename = "assistant")]
|
||||
Assistant,
|
||||
#[serde(rename = "system")]
|
||||
System,
|
||||
#[serde(rename = "tool")]
|
||||
Tool,
|
||||
}
|
||||
|
||||
impl Role {
|
||||
pub fn is_user(&self) -> bool {
|
||||
matches!(self, Role::User)
|
||||
}
|
||||
|
||||
pub fn is_assistant(&self) -> bool {
|
||||
matches!(self, Role::Assistant)
|
||||
}
|
||||
|
||||
pub fn is_system(&self) -> bool {
|
||||
matches!(self, Role::System)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ChatMessage {
|
||||
pub role: Role,
|
||||
pub content: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tool_calls: Option<Vec<super::tool::ToolCall>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tool_call_id: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
impl ChatMessage {
|
||||
pub fn user(content: impl Into<String>) -> Self {
|
||||
ChatMessage {
|
||||
role: Role::User,
|
||||
content: Some(content.into()),
|
||||
tool_calls: None,
|
||||
tool_call_id: None,
|
||||
name: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assistant(content: Option<String>) -> Self {
|
||||
ChatMessage {
|
||||
role: Role::Assistant,
|
||||
content,
|
||||
tool_calls: None,
|
||||
tool_call_id: None,
|
||||
name: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn system(content: impl Into<String>) -> Self {
|
||||
ChatMessage {
|
||||
role: Role::System,
|
||||
content: Some(content.into()),
|
||||
tool_calls: None,
|
||||
tool_call_id: None,
|
||||
name: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tool_result(tool_call_id: String, content: String) -> Self {
|
||||
ChatMessage {
|
||||
role: Role::Tool,
|
||||
content: Some(content),
|
||||
tool_calls: None,
|
||||
tool_call_id: Some(tool_call_id),
|
||||
name: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ChatMessageDisplay {
|
||||
pub role: Role,
|
||||
pub content: String,
|
||||
pub timestamp: i64,
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod message;
|
||||
pub mod tool;
|
||||
@@ -0,0 +1,34 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ToolCall {
|
||||
pub id: String,
|
||||
#[serde(rename = "type")]
|
||||
pub type_: String,
|
||||
pub function: ToolFunction,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ToolFunction {
|
||||
pub name: String,
|
||||
pub arguments: Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ToolResult {
|
||||
pub tool_call_id: String,
|
||||
pub output: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub is_error: Option<bool>,
|
||||
}
|
||||
|
||||
pub fn sanitize_tool_arguments(args: &Value) -> Value {
|
||||
match args {
|
||||
Value::String(s) => {
|
||||
serde_json::from_str(s).unwrap_or_else(|_| args.clone())
|
||||
}
|
||||
obj @ Value::Object(_) => obj.clone(),
|
||||
other => other.clone(),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod chat;
|
||||
pub mod openrouter;
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
pub mod usage;
|
||||
@@ -0,0 +1,34 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ChatRequest {
|
||||
pub model: String,
|
||||
pub messages: Vec<super::super::chat::message::ChatMessage>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub max_tokens: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub temperature: Option<f32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tools: Option<Vec<ToolDef>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub stream: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub top_p: Option<f32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub stop: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ToolDef {
|
||||
#[serde(rename = "type")]
|
||||
pub type_: String,
|
||||
pub function: ToolFunctionDef,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ToolFunctionDef {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub parameters: Value,
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ChatResponse {
|
||||
pub id: String,
|
||||
pub model: String,
|
||||
pub choices: Vec<Choice>,
|
||||
pub usage: Option<super::usage::Usage>,
|
||||
pub created: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Choice {
|
||||
pub index: u32,
|
||||
pub message: super::super::chat::message::ChatMessage,
|
||||
pub finish_reason: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct StreamChunk {
|
||||
pub id: Option<String>,
|
||||
pub model: Option<String>,
|
||||
pub choices: Vec<StreamChoice>,
|
||||
pub usage: Option<super::usage::Usage>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct StreamChoice {
|
||||
pub index: u32,
|
||||
pub delta: Delta,
|
||||
pub finish_reason: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Delta {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub role: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub content: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tool_calls: Option<Vec<DeltaToolCall>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DeltaToolCall {
|
||||
pub index: u32,
|
||||
pub id: Option<String>,
|
||||
#[serde(rename = "type")]
|
||||
pub type_: Option<String>,
|
||||
pub function: Option<DeltaFunction>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DeltaFunction {
|
||||
pub name: Option<String>,
|
||||
pub arguments: Option<String>,
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct Usage {
|
||||
pub prompt_tokens: Option<u32>,
|
||||
pub completion_tokens: Option<u32>,
|
||||
pub total_tokens: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub prompt_tokens_cost: Option<f64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub completion_tokens_cost: Option<f64>,
|
||||
}
|
||||
|
||||
impl Usage {
|
||||
pub fn total(&self) -> u32 {
|
||||
self.total_tokens.unwrap_or(0)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user