Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--require spec_helper
--pattern spec/requests/api/v1/**/*_spec.rb
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: RSpec Test Suite Exclusion Issue

The .rspec file's --pattern option restricts RSpec to only run API specs. This means running RSpec without arguments will skip all other test suites, which could lead to missed test failures for developers and CI.

Fix in Cursor Fix in Web

4 changes: 4 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,8 @@ group :test do
gem "webmock"
gem "climate_control"
gem "simplecov", require: false
gem "rspec-rails"
gem "rswag-api"
gem "rswag-specs"
gem "rswag-ui"
end
36 changes: 36 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ GEM
ruby-statistics (>= 4.0.1)
ruby2_keywords
thor (>= 0.19, < 2)
diff-lcs (1.6.2)
docile (1.4.1)
doorkeeper (5.8.2)
railties (>= 5)
Expand Down Expand Up @@ -284,6 +285,9 @@ GEM
activesupport (>= 5.0.0)
jmespath (1.6.2)
json (2.12.2)
json-schema (5.2.2)
addressable (~> 2.8)
bigdecimal (~> 3.1)
json-jwt (1.16.7)
activesupport (>= 4.2)
aes_key_wrap
Expand Down Expand Up @@ -525,6 +529,34 @@ GEM
chunky_png (~> 1.0)
rqrcode_core (~> 2.0)
rqrcode_core (2.0.0)
rspec-core (3.13.6)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.6)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-rails (8.0.2)
actionpack (>= 7.2)
activesupport (>= 7.2)
railties (>= 7.2)
rspec-core (~> 3.13)
rspec-expectations (~> 3.13)
rspec-mocks (~> 3.13)
rspec-support (~> 3.13)
rspec-support (3.13.6)
rswag-api (2.16.0)
activesupport (>= 5.2, < 8.1)
railties (>= 5.2, < 8.1)
rswag-specs (2.16.0)
activesupport (>= 5.2, < 8.1)
json-schema (>= 2.2, < 6.0)
railties (>= 5.2, < 8.1)
rspec-core (>= 2.14)
rswag-ui (2.16.0)
actionpack (>= 5.2, < 8.1)
railties (>= 5.2, < 8.1)
rubocop (1.76.1)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
Expand Down Expand Up @@ -746,6 +778,10 @@ DEPENDENCIES
redis (~> 5.4)
rotp (~> 6.3)
rqrcode (~> 3.0)
rspec-rails
rswag-api
rswag-specs
rswag-ui
rubocop-rails-omakase
ruby-lsp-rails
ruby-openai
Expand Down
237 changes: 34 additions & 203 deletions docs/api/chats.md
Original file line number Diff line number Diff line change
@@ -1,228 +1,59 @@
# Chat API Documentation

The Chat API allows external applications to interact with Sure's AI chat functionality.
The Chat API allows external applications to interact with Sure's AI chat functionality. The OpenAPI description is generated directly from executable request specs, ensuring it always reflects the behaviour of the running Rails application.

## Authentication
## Generated OpenAPI specification

All chat endpoints require authentication via OAuth2 or API keys. The chat endpoints also require the user to have AI features enabled (`ai_enabled: true`).
- The source of truth for the documentation lives in [`spec/requests/api/v1/chats_spec.rb`](../../spec/requests/api/v1/chats_spec.rb). These specs authenticate against the Rails stack, exercise every chat endpoint, and capture real response shapes.
- Regenerate the OpenAPI document with:

## Endpoints
```sh
RAILS_ENV=test bundle exec rake rswag:specs:swaggerize
```

### List Chats
```
GET /api/v1/chats
```

**Required Scope:** `read`

**Response:**
```json
{
"chats": [
{
"id": "uuid",
"title": "Chat title",
"last_message_at": "2024-01-01T00:00:00Z",
"message_count": 5,
"error": null,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
],
"pagination": {
"page": 1,
"per_page": 20,
"total_count": 50,
"total_pages": 3
}
}
```

### Get Chat
```
GET /api/v1/chats/:id
```

**Required Scope:** `read`

**Response:**
```json
{
"id": "uuid",
"title": "Chat title",
"error": null,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z",
"messages": [
{
"id": "uuid",
"type": "user_message",
"role": "user",
"content": "Hello AI",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
},
{
"id": "uuid",
"type": "assistant_message",
"role": "assistant",
"content": "Hello! How can I help you?",
"model": "gpt-4",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z",
"tool_calls": []
}
],
"pagination": {
"page": 1,
"per_page": 50,
"total_count": 2,
"total_pages": 1
}
}
```

### Create Chat
```
POST /api/v1/chats
```

**Required Scope:** `write`

**Request Body:**
```json
{
"title": "Optional chat title",
"message": "Initial message to AI",
"model": "gpt-4" // optional, defaults to gpt-4
}
```

**Response:** Same as Get Chat endpoint

### Update Chat
```
PATCH /api/v1/chats/:id
```

**Required Scope:** `write`

**Request Body:**
```json
{
"title": "New chat title"
}
```
The task compiles the request specs and writes the result to [`docs/api/openapi.yaml`](openapi.yaml).

**Response:** Same as Get Chat endpoint
- Run just the documentation specs with:

### Delete Chat
```
DELETE /api/v1/chats/:id
```

**Required Scope:** `write`

**Response:** 204 No Content

### Create Message
```
POST /api/v1/chats/:chat_id/messages
```

**Required Scope:** `write`

**Request Body:**
```json
{
"content": "User message",
"model": "gpt-4" // optional, defaults to gpt-4
}
```

**Response:**
```json
{
"id": "uuid",
"chat_id": "uuid",
"type": "user_message",
"role": "user",
"content": "User message",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z",
"ai_response_status": "pending",
"ai_response_message": "AI response is being generated"
}
```
```sh
bundle exec rspec spec/requests/api/v1/chats_spec.rb
```

### Retry Last Message
```
POST /api/v1/chats/:chat_id/messages/retry
```

**Required Scope:** `write`

Retries the last assistant message in the chat.

**Response:**
```json
{
"message": "Retry initiated",
"message_id": "uuid"
}
```
## Authentication requirements

## AI Response Handling
All chat endpoints require an OAuth2 access token or API key that grants the appropriate scope. The authenticated user must also have AI features enabled (`ai_enabled: true`).

AI responses are processed asynchronously. When you create a message or chat with an initial message, the API returns immediately with the user message. The AI response is generated in the background.
## Available endpoints

### Checking for AI Responses
| Endpoint | Scope | Description |
| --- | --- | --- |
| `GET /api/v1/chats` | `read` | List chats for the authenticated user with pagination metadata. |
| `GET /api/v1/chats/{id}` | `read` | Retrieve a chat, including ordered messages and optional pagination. |
| `POST /api/v1/chats` | `write` | Create a chat and optionally seed it with an initial user message. |
| `PATCH /api/v1/chats/{id}` | `write` | Update a chat title. |
| `DELETE /api/v1/chats/{id}` | `write` | Permanently delete a chat. |
| `POST /api/v1/chats/{chat_id}/messages` | `write` | Append a user message to a chat. |
| `POST /api/v1/chats/{chat_id}/messages/retry` | `write` | Retry the last assistant response in a chat. |

Currently, you need to poll the chat endpoint to check for new AI responses. Look for new messages with `type: "assistant_message"`.
Refer to the generated [`openapi.yaml`](openapi.yaml) for request/response schemas, reusable components (pagination, errors, messages, tool calls), and security definitions.

### Available AI Models
## AI response behaviour

- `gpt-4` (default)
- `gpt-4-turbo`
- `gpt-3.5-turbo`
- Chat creation and message submission queue AI processing jobs asynchronously; the API responds immediately with the user message payload.
- Poll `GET /api/v1/chats/{id}` to detect new assistant messages (`type: "assistant_message"`).
- Supported models today: `gpt-4` (default), `gpt-4-turbo`, and `gpt-3.5-turbo`.
- Assistant responses may include structured tool calls (`tool_calls`) that reference financial data fetches and their results.

### Tool Calls
## Error responses

The AI assistant can make tool calls to access user financial data. These appear in the `tool_calls` array of assistant messages:

```json
{
"tool_calls": [
{
"id": "uuid",
"function_name": "get_accounts",
"function_arguments": {},
"function_result": { ... },
"created_at": "2024-01-01T00:00:00Z"
}
]
}
```

## Error Handling

All endpoints return standard error responses:
Errors conform to the shared `ErrorResponse` schema in the OpenAPI document:

```json
{
"error": "error_code",
"message": "Human readable error message",
"details": ["Additional error details"] // optional
"details": ["Optional array of extra context"]
}
```

Common error codes:
- `unauthorized` - Invalid or missing authentication
- `forbidden` - Insufficient permissions or AI not enabled
- `not_found` - Resource not found
- `unprocessable_entity` - Invalid request data
- `rate_limit_exceeded` - Too many requests

## Rate Limits

Chat API endpoints are subject to the standard API rate limits based on your API key tier.
Common error codes include `unauthorized`, `forbidden`, `feature_disabled`, `not_found`, `unprocessable_entity`, and `rate_limit_exceeded`.
Loading