Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 157 additions & 3 deletions crates/aionui-api-types/src/assistant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ use serde::{Deserialize, Serialize};
pub enum AssistantSource {
Builtin,
User,
Extension,
}

/// Wire shape returned by `GET /api/assistants` (single element) and
Expand Down Expand Up @@ -58,6 +57,132 @@ pub struct AssistantResponse {
pub last_used_at: Option<i64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantProfileResponse {
pub name: String,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub name_i18n: HashMap<String, String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub description_i18n: HashMap<String, String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub avatar: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantStateResponse {
pub enabled: bool,
pub sort_order: i32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_used_at: Option<i64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantEngineResponse {
pub agent_backend: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantRulesResponse {
pub content: String,
pub storage_mode: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantPromptsResponse {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub recommended: Vec<String>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub recommended_i18n: HashMap<String, Vec<String>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantDefaultScalarResponse {
pub mode: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub value: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantDefaultListResponse {
pub mode: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub value: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantDefaultScalarRequest {
pub mode: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub value: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantDefaultListRequest {
pub mode: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub value: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AssistantDefaultsRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model: Option<AssistantDefaultScalarRequest>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub permission: Option<AssistantDefaultScalarRequest>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub skills: Option<AssistantDefaultListRequest>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mcps: Option<AssistantDefaultListRequest>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantDefaultsResponse {
pub model: AssistantDefaultScalarResponse,
pub permission: AssistantDefaultScalarResponse,
pub skills: AssistantDefaultListResponse,
pub mcps: AssistantDefaultListResponse,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantCapabilitiesResponse {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub default_skill_ids: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub custom_skill_names: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub default_disabled_builtin_skill_ids: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantPreferencesResponse {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_model_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_permission_value: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub last_skill_ids: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub last_disabled_builtin_skill_ids: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub last_mcp_ids: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantDetailResponse {
pub id: String,
pub source: AssistantSource,
pub profile: AssistantProfileResponse,
pub state: AssistantStateResponse,
pub engine: AssistantEngineResponse,
pub rules: AssistantRulesResponse,
pub prompts: AssistantPromptsResponse,
pub defaults: AssistantDefaultsResponse,
pub capabilities: AssistantCapabilitiesResponse,
pub preferences: AssistantPreferencesResponse,
}

// ---------------------------------------------------------------------------
// Request types
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -90,6 +215,12 @@ pub struct CreateAssistantRequest {
pub description_i18n: Option<HashMap<String, String>>,
#[serde(default)]
pub prompts_i18n: Option<HashMap<String, Vec<String>>>,
#[serde(default)]
pub recommended_prompts: Option<Vec<String>>,
#[serde(default)]
pub recommended_prompts_i18n: Option<HashMap<String, Vec<String>>>,
#[serde(default)]
pub defaults: Option<AssistantDefaultsRequest>,
}

/// `PUT /api/assistants/{id}`. All fields optional; partial update semantics.
Expand Down Expand Up @@ -119,6 +250,12 @@ pub struct UpdateAssistantRequest {
pub description_i18n: Option<HashMap<String, String>>,
#[serde(default)]
pub prompts_i18n: Option<HashMap<String, Vec<String>>>,
#[serde(default)]
pub recommended_prompts: Option<Vec<String>>,
#[serde(default)]
pub recommended_prompts_i18n: Option<HashMap<String, Vec<String>>>,
#[serde(default)]
pub defaults: Option<AssistantDefaultsRequest>,
}

/// `PATCH /api/assistants/{id}/state`. Upserts `assistant_overrides`.
Expand Down Expand Up @@ -166,8 +303,6 @@ mod tests {
assert_eq!(json, "\"builtin\"");
let json = serde_json::to_string(&AssistantSource::User).unwrap();
assert_eq!(json, "\"user\"");
let json = serde_json::to_string(&AssistantSource::Extension).unwrap();
assert_eq!(json, "\"extension\"");
}

#[test]
Expand Down Expand Up @@ -207,6 +342,7 @@ mod tests {
assert_eq!(req.name, "X");
assert!(req.id.is_none());
assert!(req.preset_agent_type.is_none());
assert!(req.defaults.is_none());
}

#[test]
Expand All @@ -215,6 +351,24 @@ mod tests {
let req: UpdateAssistantRequest = serde_json::from_value(json).unwrap();
assert_eq!(req.name.as_deref(), Some("renamed"));
assert!(req.description.is_none());
assert!(req.defaults.is_none());
}

#[test]
fn create_request_accepts_defaults_and_recommended_prompts() {
let json = serde_json::json!({
"name": "planner",
"recommended_prompts": ["Plan work"],
"defaults": {
"model": { "mode": "fixed", "value": "openai/gpt-5" },
"skills": { "mode": "fixed", "value": ["skill-a"] }
}
});
let req: CreateAssistantRequest = serde_json::from_value(json).unwrap();
assert_eq!(req.recommended_prompts.unwrap(), vec!["Plan work"]);
let defaults = req.defaults.unwrap();
assert_eq!(defaults.model.unwrap().mode, "fixed");
assert_eq!(defaults.skills.unwrap().value, vec!["skill-a"]);
}

#[test]
Expand Down
50 changes: 50 additions & 0 deletions crates/aionui-api-types/src/conversation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,36 @@ pub struct ConversationMcpStatus {

// ── Request types ──────────────────────────────────────────────────

#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Default)]
pub struct AssistantConversationOverridesRequest {
#[serde(default)]
pub model: Option<String>,
#[serde(default)]
pub permission: Option<String>,
#[serde(default)]
pub skill_ids: Option<Vec<String>>,
#[serde(default)]
pub disabled_builtin_skill_ids: Option<Vec<String>>,
#[serde(default)]
pub mcp_ids: Option<Vec<String>>,
}

#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub struct AssistantConversationRequest {
pub id: String,
#[serde(default)]
pub locale: Option<String>,
#[serde(default)]
pub conversation_overrides: Option<AssistantConversationOverridesRequest>,
}

/// Body for `POST /api/conversations`.
#[derive(Debug, Deserialize)]
pub struct CreateConversationRequest {
pub r#type: AgentType,
pub name: Option<String>,
pub model: Option<ProviderWithModel>,
pub assistant: Option<AssistantConversationRequest>,
pub source: Option<ConversationSource>,
pub channel_chat_id: Option<String>,
pub extra: serde_json::Value,
Expand Down Expand Up @@ -269,6 +293,17 @@ mod tests {
"type": "acp",
"name": "Code Review",
"model": { "provider_id": "p1", "model": "claude-sonnet-4-20250514" },
"assistant": {
"id": "assistant-1",
"locale": "zh-CN",
"conversation_overrides": {
"model": "opus-4.1",
"permission": "yolo",
"skill_ids": ["skill-a"],
"disabled_builtin_skill_ids": ["builtin-a"],
"mcp_ids": ["mcp-a"]
}
},
"source": "aionui",
"channel_chat_id": "user:123",
"extra": { "workspace": "/project" }
Expand All @@ -277,6 +312,20 @@ mod tests {
assert_eq!(req.r#type, AgentType::Acp);
assert_eq!(req.name.as_deref(), Some("Code Review"));
assert_eq!(req.model.unwrap().model, "claude-sonnet-4-20250514");
assert_eq!(
req.assistant,
Some(AssistantConversationRequest {
id: "assistant-1".into(),
locale: Some("zh-CN".into()),
conversation_overrides: Some(AssistantConversationOverridesRequest {
model: Some("opus-4.1".into()),
permission: Some("yolo".into()),
skill_ids: Some(vec!["skill-a".into()]),
disabled_builtin_skill_ids: Some(vec!["builtin-a".into()]),
mcp_ids: Some(vec!["mcp-a".into()]),
}),
})
);
assert_eq!(req.source, Some(ConversationSource::Aionui));
assert_eq!(req.channel_chat_id.as_deref(), Some("user:123"));
assert_eq!(req.extra["workspace"], "/project");
Expand All @@ -292,6 +341,7 @@ mod tests {
let req: CreateConversationRequest = serde_json::from_value(raw).unwrap();
assert_eq!(req.r#type, AgentType::Acp);
assert!(req.name.is_none());
assert!(req.assistant.is_none());
assert!(req.source.is_none());
assert!(req.channel_chat_id.is_none());
}
Expand Down
23 changes: 14 additions & 9 deletions crates/aionui-api-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,12 @@ pub use agent_error::{
AgentStreamErrorData,
};
pub use assistant::{
AssistantResponse, AssistantSource, CreateAssistantRequest, ImportAssistantsRequest, ImportAssistantsResult,
ImportError, SetAssistantStateRequest, UpdateAssistantRequest,
AssistantCapabilitiesResponse, AssistantDefaultListRequest, AssistantDefaultListResponse,
AssistantDefaultScalarRequest, AssistantDefaultScalarResponse, AssistantDefaultsRequest, AssistantDefaultsResponse,
AssistantDetailResponse, AssistantEngineResponse, AssistantPreferencesResponse, AssistantProfileResponse,
AssistantPromptsResponse, AssistantResponse, AssistantRulesResponse, AssistantSource, AssistantStateResponse,
CreateAssistantRequest, ImportAssistantsRequest, ImportAssistantsResult, ImportError, SetAssistantStateRequest,
UpdateAssistantRequest,
};
pub use auth::{
AuthStatusResponse, ChangePasswordRequest, LoginRequest, LoginResponse, PublicUser, QrLoginRequest,
Expand All @@ -64,13 +68,14 @@ pub use channel::{
pub use confirmation::{ApprovalCheckQuery, ApprovalCheckResponse, ConfirmRequest, ConfirmationListResponse};
pub use connection_test::TestBedrockConnectionRequest;
pub use conversation::{
ActiveCountResponse, CancelConversationRequest, CancelConversationResponse, CloneConversationRequest,
ConversationArtifactKind, ConversationArtifactListResponse, ConversationArtifactResponse,
ConversationArtifactStatus, ConversationListResponse, ConversationMcpStatus, ConversationMcpStatusKind,
ConversationResponse, ConversationRuntimeStateKind, ConversationRuntimeSummary, CreateConversationRequest,
ListConversationsQuery, ListMessagesQuery, MessageListResponse, MessageResponse, MessageSearchItem,
MessageSearchResponse, SearchMessagesQuery, SendMessageRequest, SendMessageResponse,
UpdateConversationArtifactRequest, UpdateConversationRequest,
ActiveCountResponse, AssistantConversationOverridesRequest, AssistantConversationRequest,
CancelConversationRequest, CancelConversationResponse, CloneConversationRequest, ConversationArtifactKind,
ConversationArtifactListResponse, ConversationArtifactResponse, ConversationArtifactStatus,
ConversationListResponse, ConversationMcpStatus, ConversationMcpStatusKind, ConversationResponse,
ConversationRuntimeStateKind, ConversationRuntimeSummary, CreateConversationRequest, ListConversationsQuery,
ListMessagesQuery, MessageListResponse, MessageResponse, MessageSearchItem, MessageSearchResponse,
SearchMessagesQuery, SendMessageRequest, SendMessageResponse, UpdateConversationArtifactRequest,
UpdateConversationRequest,
};
pub use cron::{
CreateCronJobRequest, CronAgentConfigDto, CronJobExecutedEvent, CronJobMetadataDto, CronJobPayloadDto,
Expand Down
Loading
Loading