mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-10 03:30:59 +00:00
Sessions API review fixes: add updated_at to create response, UUID session IDs, remove misleading SSE schemas
- Add updated_at to CreateSessionResponse for consistency with SessionListItem/SessionDetail - Add format: uuid to session ID fields and parameter across the OpenAPI spec - Remove SessionEvent discriminated union and SessionEvent* wrapper schemas that conflated SSE transport-level event names with JSON data payload fields - Update demo data to use proper UUIDs instead of string IDs - Add uuid dependency to arc-types crate Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3a2e91a123
commit
92dc29eb6f
8 changed files with 60 additions and 101 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -317,6 +317,7 @@ dependencies = [
|
|||
"serde_yaml",
|
||||
"syn",
|
||||
"typify",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5069,6 +5070,7 @@ checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb"
|
|||
dependencies = [
|
||||
"getrandom 0.4.1",
|
||||
"js-sys",
|
||||
"serde_core",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -501,9 +501,11 @@ pub async fn create_session_stub(
|
|||
_auth: AuthenticatedService,
|
||||
State(_state): State<Arc<AppState>>,
|
||||
) -> Response {
|
||||
let id = uuid::Uuid::new_v4();
|
||||
let now = "2026-03-06T16:00:00Z";
|
||||
(
|
||||
StatusCode::CREATED,
|
||||
Json(serde_json::json!({"id": "demo-session-new", "title": "New session", "model": "Opus 4.6", "created_at": "2026-03-06T16:00:00Z"})),
|
||||
Json(serde_json::json!({"id": id, "title": "New session", "model": "Opus 4.6", "created_at": now, "updated_at": now})),
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
|
|
@ -2367,15 +2369,29 @@ mod retros {
|
|||
mod sessions {
|
||||
use arc_types::*;
|
||||
use chrono::{DateTime, Utc};
|
||||
use uuid::Uuid;
|
||||
|
||||
fn ts(s: &str) -> DateTime<Utc> {
|
||||
s.parse().unwrap()
|
||||
}
|
||||
|
||||
fn uid(n: u128) -> Uuid {
|
||||
Uuid::from_u128(n)
|
||||
}
|
||||
|
||||
const S1: u128 = 0x10000000_0000_4000_8000_000000000001;
|
||||
const S2: u128 = 0x10000000_0000_4000_8000_000000000002;
|
||||
const S3: u128 = 0x10000000_0000_4000_8000_000000000003;
|
||||
const S4: u128 = 0x10000000_0000_4000_8000_000000000004;
|
||||
const S5: u128 = 0x10000000_0000_4000_8000_000000000005;
|
||||
const S6: u128 = 0x10000000_0000_4000_8000_000000000006;
|
||||
const S7: u128 = 0x10000000_0000_4000_8000_000000000007;
|
||||
const S8: u128 = 0x10000000_0000_4000_8000_000000000008;
|
||||
|
||||
pub fn list_items() -> Vec<SessionListItem> {
|
||||
vec![
|
||||
SessionListItem {
|
||||
id: "s1".into(),
|
||||
id: uid(S1),
|
||||
title: "Add rate limiting to auth endpoints".into(),
|
||||
model: "Opus 4.6".into(),
|
||||
last_message_preview: "Done. I've created the rate limiter and wired it up...".into(),
|
||||
|
|
@ -2383,7 +2399,7 @@ mod sessions {
|
|||
updated_at: ts("2026-03-06T15:45:00Z"),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s2".into(),
|
||||
id: uid(S2),
|
||||
title: "Fix config parsing for nested values".into(),
|
||||
model: "Sonnet 4.6".into(),
|
||||
last_message_preview: "Fixed. The parser now tracks the current section header...".into(),
|
||||
|
|
@ -2391,7 +2407,7 @@ mod sessions {
|
|||
updated_at: ts("2026-03-06T13:15:00Z"),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s3".into(),
|
||||
id: uid(S3),
|
||||
title: "Migrate to React Router v7".into(),
|
||||
model: "Opus 4.6".into(),
|
||||
last_message_preview: "You're on React Router 6.22. The migration to v7 involves...".into(),
|
||||
|
|
@ -2399,7 +2415,7 @@ mod sessions {
|
|||
updated_at: ts("2026-03-05T11:30:00Z"),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s4".into(),
|
||||
id: uid(S4),
|
||||
title: "Add dark mode toggle".into(),
|
||||
model: "Sonnet 4.6".into(),
|
||||
last_message_preview: "Added a dark mode toggle to the settings panel with system preference detection.".into(),
|
||||
|
|
@ -2407,7 +2423,7 @@ mod sessions {
|
|||
updated_at: ts("2026-03-05T09:45:00Z"),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s5".into(),
|
||||
id: uid(S5),
|
||||
title: "Update OpenAPI spec for v3".into(),
|
||||
model: "Opus 4.6".into(),
|
||||
last_message_preview: "Updated all endpoint schemas to v3 format with discriminated unions.".into(),
|
||||
|
|
@ -2415,7 +2431,7 @@ mod sessions {
|
|||
updated_at: ts("2026-03-05T08:30:00Z"),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s6".into(),
|
||||
id: uid(S6),
|
||||
title: "Terraform module for Redis cluster".into(),
|
||||
model: "Opus 4.6".into(),
|
||||
last_message_preview: "Created the module with 3-node cluster, automatic failover, and encryption at rest.".into(),
|
||||
|
|
@ -2423,7 +2439,7 @@ mod sessions {
|
|||
updated_at: ts("2026-03-03T16:00:00Z"),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s7".into(),
|
||||
id: uid(S7),
|
||||
title: "Add pipeline event types".into(),
|
||||
model: "Sonnet 4.6".into(),
|
||||
last_message_preview: "Added PipelineStarted, StageCompleted, and PipelineFailed event types.".into(),
|
||||
|
|
@ -2431,7 +2447,7 @@ mod sessions {
|
|||
updated_at: ts("2026-03-01T12:00:00Z"),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s8".into(),
|
||||
id: uid(S8),
|
||||
title: "Implement webhook retry logic".into(),
|
||||
model: "Opus 4.6".into(),
|
||||
last_message_preview: "Implemented exponential backoff with jitter, max 5 retries over 24 hours.".into(),
|
||||
|
|
@ -2442,9 +2458,10 @@ mod sessions {
|
|||
}
|
||||
|
||||
pub fn detail(id: &str) -> Option<SessionDetail> {
|
||||
match id {
|
||||
"s1" => Some(SessionDetail {
|
||||
id: "s1".into(), title: "Add rate limiting to auth endpoints".into(), model: "Opus 4.6".into(), created_at: ts("2026-03-06T14:30:00Z"), updated_at: ts("2026-03-06T15:45:00Z"),
|
||||
let parsed = id.parse::<Uuid>().ok()?;
|
||||
match parsed.as_u128() {
|
||||
S1 => Some(SessionDetail {
|
||||
id: uid(S1), title: "Add rate limiting to auth endpoints".into(), model: "Opus 4.6".into(), created_at: ts("2026-03-06T14:30:00Z"), updated_at: ts("2026-03-06T15:45:00Z"),
|
||||
turns: vec![
|
||||
SessionTurn::UserTurn(UserTurn { kind: UserTurnKind::User, content: "Add rate limiting to the auth endpoints. We're getting hit with brute force attempts on /api/auth/login and /api/auth/register. Use a sliding window approach with Redis, 10 requests per minute per IP.".into(), created_at: ts("2026-02-28T10:00:00Z") }),
|
||||
SessionTurn::AssistantTurn(AssistantTurn { kind: AssistantTurnKind::Assistant, content: "I'll implement sliding window rate limiting using Redis. Let me first look at the existing auth routes and middleware setup.".into(), created_at: ts("2026-02-28T10:00:15Z") }),
|
||||
|
|
@ -2462,8 +2479,8 @@ mod sessions {
|
|||
SessionTurn::AssistantTurn(AssistantTurn { kind: AssistantTurnKind::Assistant, content: "Done. I've created the rate limiter and wired it up:\n\n- **`src/middleware/rate-limit.ts`** \u{2014} Sliding window rate limiter using Redis sorted sets. Each request adds a timestamped entry, expired entries are pruned, and the count is checked against the limit.\n- **`src/routes/auth.ts`** \u{2014} Applied `rateLimit({ windowMs: 60_000, max: 10 })` to both `/login` and `/register`.\n- **`src/middleware/index.ts`** \u{2014} Re-exported the new middleware.\n\nWhen the limit is exceeded, clients get a `429` response with a `Retry-After` header indicating seconds until the window resets.".into(), created_at: ts("2026-02-28T10:01:00Z") }),
|
||||
],
|
||||
}),
|
||||
"s2" => Some(SessionDetail {
|
||||
id: "s2".into(), title: "Fix config parsing for nested values".into(), model: "Sonnet 4.6".into(), created_at: ts("2026-03-06T12:30:00Z"), updated_at: ts("2026-03-06T13:15:00Z"),
|
||||
S2 => Some(SessionDetail {
|
||||
id: uid(S2), title: "Fix config parsing for nested values".into(), model: "Sonnet 4.6".into(), created_at: ts("2026-03-06T12:30:00Z"), updated_at: ts("2026-03-06T13:15:00Z"),
|
||||
turns: vec![
|
||||
SessionTurn::UserTurn(UserTurn { kind: UserTurnKind::User, content: "The CLI crashes when parsing nested TOML config values like [database.connection]. Can you debug and fix this?".into(), created_at: ts("2026-02-28T10:00:00Z") }),
|
||||
SessionTurn::AssistantTurn(AssistantTurn { kind: AssistantTurnKind::Assistant, content: "Let me look at the config parser to understand how nested keys are handled.".into(), created_at: ts("2026-02-28T10:00:12Z") }),
|
||||
|
|
@ -2478,8 +2495,8 @@ mod sessions {
|
|||
SessionTurn::AssistantTurn(AssistantTurn { kind: AssistantTurnKind::Assistant, content: "Fixed. The parser now:\n\n1. Tracks the current section header (e.g. `[database.connection]`)\n2. Constructs nested objects from dotted paths\n3. Handles both `key = value` within sections and bare `dotted.key = value` syntax\n\nAll 4 tests pass, including the new nested section case.".into(), created_at: ts("2026-02-28T10:01:00Z") }),
|
||||
],
|
||||
}),
|
||||
"s3" => Some(SessionDetail {
|
||||
id: "s3".into(), title: "Migrate to React Router v7".into(), model: "Opus 4.6".into(), created_at: ts("2026-03-05T10:00:00Z"), updated_at: ts("2026-03-05T11:30:00Z"),
|
||||
S3 => Some(SessionDetail {
|
||||
id: uid(S3), title: "Migrate to React Router v7".into(), model: "Opus 4.6".into(), created_at: ts("2026-03-05T10:00:00Z"), updated_at: ts("2026-03-05T11:30:00Z"),
|
||||
turns: vec![
|
||||
SessionTurn::UserTurn(UserTurn { kind: UserTurnKind::User, content: "Help me migrate our app from React Router v6 to v7. We're using createBrowserRouter with data loaders.".into(), created_at: ts("2026-02-26T10:00:00Z") }),
|
||||
SessionTurn::AssistantTurn(AssistantTurn { kind: AssistantTurnKind::Assistant, content: "I'll audit your current router setup and identify what needs to change for v7. Let me scan the codebase.".into(), created_at: ts("2026-02-26T10:00:10Z") }),
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ doctest = false
|
|||
chrono = { workspace = true, features = ["serde"] }
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
uuid = { workspace = true, features = ["serde"] }
|
||||
|
||||
[build-dependencies]
|
||||
typify = { version = "0.6", default-features = false }
|
||||
|
|
|
|||
|
|
@ -814,14 +814,14 @@ paths:
|
|||
description: |
|
||||
Opens a server-sent event (SSE) stream for real-time session updates.
|
||||
|
||||
The stream emits the following event types:
|
||||
The stream emits the following SSE event types:
|
||||
|
||||
- `event: assistant_turn` — data: `AssistantTurn` JSON object
|
||||
- `event: tool_turn` — data: `ToolTurn` JSON object
|
||||
- `event: done` — data: `{}` (stream complete)
|
||||
- `event: error` — data: `{"message": "..."}` (error occurred)
|
||||
|
||||
See the `SessionEvent` schema for the discriminated union of all event payloads.
|
||||
Each SSE frame has an `event:` line (the event type) and a `data:` line (the JSON payload).
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/SessionId"
|
||||
responses:
|
||||
|
|
@ -1072,7 +1072,8 @@ components:
|
|||
description: Unique session identifier.
|
||||
schema:
|
||||
type: string
|
||||
example: s1
|
||||
format: uuid
|
||||
example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
||||
|
||||
PageLimit:
|
||||
name: page[limit]
|
||||
|
|
@ -2091,8 +2092,9 @@ components:
|
|||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Unique session identifier.
|
||||
example: s1
|
||||
example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
||||
title:
|
||||
type: string
|
||||
description: Short title summarizing the session topic.
|
||||
|
|
@ -2207,8 +2209,9 @@ components:
|
|||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Unique session identifier.
|
||||
example: s1
|
||||
example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
||||
title:
|
||||
type: string
|
||||
description: Short title summarizing the session topic.
|
||||
|
|
@ -2256,11 +2259,13 @@ components:
|
|||
- title
|
||||
- model
|
||||
- created_at
|
||||
- updated_at
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Unique identifier for the newly created session.
|
||||
example: s42
|
||||
example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
||||
title:
|
||||
type: string
|
||||
description: Server-generated title for the session.
|
||||
|
|
@ -2274,6 +2279,11 @@ components:
|
|||
format: date-time
|
||||
description: Timestamp when the session was created.
|
||||
example: "2026-03-06T16:00:00Z"
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp when the session was last updated (equal to created_at at creation time).
|
||||
example: "2026-03-06T16:00:00Z"
|
||||
|
||||
SendMessageRequest:
|
||||
description: Request body for sending a follow-up message in an existing session.
|
||||
|
|
@ -2297,71 +2307,6 @@ components:
|
|||
description: Whether the message was accepted for processing.
|
||||
example: true
|
||||
|
||||
SessionEvent:
|
||||
description: Discriminated union of all SSE event payloads.
|
||||
discriminator:
|
||||
propertyName: event
|
||||
mapping:
|
||||
assistant_turn: "#/components/schemas/SessionEventAssistantTurn"
|
||||
tool_turn: "#/components/schemas/SessionEventToolTurn"
|
||||
done: "#/components/schemas/SessionEventDone"
|
||||
error: "#/components/schemas/SessionEventError"
|
||||
oneOf:
|
||||
- $ref: "#/components/schemas/SessionEventAssistantTurn"
|
||||
- $ref: "#/components/schemas/SessionEventToolTurn"
|
||||
- $ref: "#/components/schemas/SessionEventDone"
|
||||
- $ref: "#/components/schemas/SessionEventError"
|
||||
|
||||
SessionEventAssistantTurn:
|
||||
description: An assistant turn event.
|
||||
type: object
|
||||
required:
|
||||
- event
|
||||
- data
|
||||
properties:
|
||||
event:
|
||||
type: string
|
||||
enum: [assistant_turn]
|
||||
data:
|
||||
$ref: "#/components/schemas/AssistantTurn"
|
||||
|
||||
SessionEventToolTurn:
|
||||
description: A tool turn event.
|
||||
type: object
|
||||
required:
|
||||
- event
|
||||
- data
|
||||
properties:
|
||||
event:
|
||||
type: string
|
||||
enum: [tool_turn]
|
||||
data:
|
||||
$ref: "#/components/schemas/ToolTurn"
|
||||
|
||||
SessionEventDone:
|
||||
description: Stream completion event.
|
||||
type: object
|
||||
required:
|
||||
- event
|
||||
properties:
|
||||
event:
|
||||
type: string
|
||||
enum: [done]
|
||||
|
||||
SessionEventError:
|
||||
description: Error event.
|
||||
type: object
|
||||
required:
|
||||
- event
|
||||
- message
|
||||
properties:
|
||||
event:
|
||||
type: string
|
||||
enum: [error]
|
||||
message:
|
||||
type: string
|
||||
description: Human-readable error message.
|
||||
|
||||
# ── Insights Schemas ────────────────────────────────────────────────
|
||||
|
||||
SavedQuery:
|
||||
|
|
|
|||
|
|
@ -78,11 +78,6 @@ models/saved-query.ts
|
|||
models/send-message-request.ts
|
||||
models/send-message-response.ts
|
||||
models/session-detail.ts
|
||||
models/session-event-assistant-turn.ts
|
||||
models/session-event-done.ts
|
||||
models/session-event-error.ts
|
||||
models/session-event-tool-turn.ts
|
||||
models/session-event.ts
|
||||
models/session-list-item.ts
|
||||
models/session-turn.ts
|
||||
models/setting-field-type.ts
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ export const SessionsApiAxiosParamCreator = function (configuration?: Configurat
|
|||
};
|
||||
},
|
||||
/**
|
||||
* Opens a server-sent event (SSE) stream for real-time session updates. The stream emits the following event types: - `event: assistant_turn` — data: `AssistantTurn` JSON object - `event: tool_turn` — data: `ToolTurn` JSON object - `event: done` — data: `{}` (stream complete) - `event: error` — data: `{\"message\": \"...\"}` (error occurred) See the `SessionEvent` schema for the discriminated union of all event payloads.
|
||||
* Opens a server-sent event (SSE) stream for real-time session updates. The stream emits the following SSE event types: - `event: assistant_turn` — data: `AssistantTurn` JSON object - `event: tool_turn` — data: `ToolTurn` JSON object - `event: done` — data: `{}` (stream complete) - `event: error` — data: `{\"message\": \"...\"}` (error occurred) Each SSE frame has an `event:` line (the event type) and a `data:` line (the JSON payload).
|
||||
* @summary Stream Session Events
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
@ -321,7 +321,7 @@ export const SessionsApiFp = function(configuration?: Configuration) {
|
|||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Opens a server-sent event (SSE) stream for real-time session updates. The stream emits the following event types: - `event: assistant_turn` — data: `AssistantTurn` JSON object - `event: tool_turn` — data: `ToolTurn` JSON object - `event: done` — data: `{}` (stream complete) - `event: error` — data: `{\"message\": \"...\"}` (error occurred) See the `SessionEvent` schema for the discriminated union of all event payloads.
|
||||
* Opens a server-sent event (SSE) stream for real-time session updates. The stream emits the following SSE event types: - `event: assistant_turn` — data: `AssistantTurn` JSON object - `event: tool_turn` — data: `ToolTurn` JSON object - `event: done` — data: `{}` (stream complete) - `event: error` — data: `{\"message\": \"...\"}` (error occurred) Each SSE frame has an `event:` line (the event type) and a `data:` line (the JSON payload).
|
||||
* @summary Stream Session Events
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
@ -385,7 +385,7 @@ export const SessionsApiFactory = function (configuration?: Configuration, baseP
|
|||
return localVarFp.sendSessionMessage(id, sendMessageRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Opens a server-sent event (SSE) stream for real-time session updates. The stream emits the following event types: - `event: assistant_turn` — data: `AssistantTurn` JSON object - `event: tool_turn` — data: `ToolTurn` JSON object - `event: done` — data: `{}` (stream complete) - `event: error` — data: `{\"message\": \"...\"}` (error occurred) See the `SessionEvent` schema for the discriminated union of all event payloads.
|
||||
* Opens a server-sent event (SSE) stream for real-time session updates. The stream emits the following SSE event types: - `event: assistant_turn` — data: `AssistantTurn` JSON object - `event: tool_turn` — data: `ToolTurn` JSON object - `event: done` — data: `{}` (stream complete) - `event: error` — data: `{\"message\": \"...\"}` (error occurred) Each SSE frame has an `event:` line (the event type) and a `data:` line (the JSON payload).
|
||||
* @summary Stream Session Events
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
@ -448,7 +448,7 @@ export class SessionsApi extends BaseAPI {
|
|||
}
|
||||
|
||||
/**
|
||||
* Opens a server-sent event (SSE) stream for real-time session updates. The stream emits the following event types: - `event: assistant_turn` — data: `AssistantTurn` JSON object - `event: tool_turn` — data: `ToolTurn` JSON object - `event: done` — data: `{}` (stream complete) - `event: error` — data: `{\"message\": \"...\"}` (error occurred) See the `SessionEvent` schema for the discriminated union of all event payloads.
|
||||
* Opens a server-sent event (SSE) stream for real-time session updates. The stream emits the following SSE event types: - `event: assistant_turn` — data: `AssistantTurn` JSON object - `event: tool_turn` — data: `ToolTurn` JSON object - `event: done` — data: `{}` (stream complete) - `event: error` — data: `{\"message\": \"...\"}` (error occurred) Each SSE frame has an `event:` line (the event type) and a `data:` line (the JSON payload).
|
||||
* @summary Stream Session Events
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
|
|||
|
|
@ -34,5 +34,9 @@ export interface CreateSessionResponse {
|
|||
* Timestamp when the session was created.
|
||||
*/
|
||||
'created_at': string;
|
||||
/**
|
||||
* Timestamp when the session was last updated (equal to created_at at creation time).
|
||||
*/
|
||||
'updated_at': string;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,11 +59,6 @@ export * from './saved-query';
|
|||
export * from './send-message-request';
|
||||
export * from './send-message-response';
|
||||
export * from './session-detail';
|
||||
export * from './session-event';
|
||||
export * from './session-event-assistant-turn';
|
||||
export * from './session-event-done';
|
||||
export * from './session-event-error';
|
||||
export * from './session-event-tool-turn';
|
||||
export * from './session-list-item';
|
||||
export * from './session-turn';
|
||||
export * from './setting-field';
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue