feat: Introduce a comprehensive set of AI agent skills, agents, and workflows, along with new UI components and application structure.
This commit is contained in:
1 parent
a2a512d41d
commit
93df87d7e9
317 files changed
+63716
-8727
No files matched your search
@@ -0,0 +1,168 @@
|
||||
# PLAN: Chat UX Improvements + Streaming Fix
|
||||
|
||||
> 🤖 **Agentes:** `@frontend-specialist` + `@mobile-developer`
|
||||
> **Data:** 2026-03-04
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Bug Crítico: ReadableStream not supported
|
||||
|
||||
**Causa raiz:** O `fetch` nativo do React Native (Hermes) não suporta `response.body.getReader()`. A implementação atual usa a Web Streams API que só funciona em navegadores.
|
||||
|
||||
**Solução:** Usar o pacote `react-native-sse` — cliente SSE leve feito para React Native, que usa `XMLHttpRequest` internamente (sem polyfills pesados).
|
||||
|
||||
```bash
|
||||
pnpm add react-native-sse
|
||||
```
|
||||
|
||||
Reescrever `chatCompletionStream()` em `services/lmStudioApi.ts` para usar `EventSource` do `react-native-sse` no mobile e manter `ReadableStream` no web via `Platform.OS`.
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Brainstorm: Rendering de Markdown
|
||||
|
||||
### Contexto
|
||||
Respostas de LLMs vêm em Markdown (code blocks, bold, listas, etc). Atualmente renderizamos como `<Text>` puro.
|
||||
|
||||
### Option A: `react-native-markdown-display`
|
||||
- ✅ Leve, customizável por StyleSheet, suporta code blocks
|
||||
- ❌ Manutenção irregular
|
||||
- 📊 **Effort:** Low
|
||||
|
||||
### Option B: `@ronradtke/react-native-markdown-display` (fork mantido)
|
||||
- ✅ Fork ativo e mantido, API idêntica, melhor suporte Expo
|
||||
- ✅ Customização completa de estilos
|
||||
- ❌ Sem syntax highlighting nativo (adicionável)
|
||||
- 📊 **Effort:** Low
|
||||
|
||||
### Option C: Custom parser com regex
|
||||
- ✅ Zero dependências, 100% controle
|
||||
- ❌ Muito trabalho, bugs com edge cases
|
||||
- 📊 **Effort:** High
|
||||
|
||||
### 💡 Recomendação
|
||||
**Option B** (`@ronradtke/react-native-markdown-display`) — fork mantido, baixo esforço e cobre 95% dos casos.
|
||||
|
||||
---
|
||||
|
||||
## 📋 Plano de Implementação
|
||||
|
||||
### Fase 1: Bug Fix — Streaming (Prioridade Máxima)
|
||||
|
||||
#### [MODIFY] `services/lmStudioApi.ts`
|
||||
- Instalar `react-native-sse`
|
||||
- Reescrever `chatCompletionStream()` com dual strategy:
|
||||
- **Web:** Manter `ReadableStream` (funciona nativamente)
|
||||
- **Native (iOS/Android):** Usar SSE via `react-native-sse`
|
||||
- Handling robusto de erros e abort
|
||||
|
||||
---
|
||||
|
||||
### Fase 2: Markdown Rendering
|
||||
|
||||
#### [ADD] Dependência
|
||||
```bash
|
||||
pnpm add @ronradtke/react-native-markdown-display
|
||||
```
|
||||
|
||||
#### [MODIFY] `components/chat/MessageBubble.tsx`
|
||||
- Substituir `<Text>` por `<Markdown>` nas mensagens do assistant
|
||||
- Manter `<Text>` para mensagens do user (são texto simples)
|
||||
- Estilizar code blocks com fundo escuro, fonte mono
|
||||
- Estilizar bold, italic, listas, links
|
||||
- Suporte a inline code com badge de destaque
|
||||
|
||||
---
|
||||
|
||||
### Fase 3: Message Actions (Copy, Edit, Resend)
|
||||
|
||||
#### [MODIFY] `components/chat/MessageBubble.tsx`
|
||||
- Adicionar toolbar de ações abaixo de cada mensagem (aparece via long-press ou sempre visível para assistant)
|
||||
- **Actions do Assistant:**
|
||||
- 📋 Copy — copia conteúdo para clipboard (`Clipboard.setStringAsync`)
|
||||
- 🔄 Regenerate — reenvia a última mensagem do user
|
||||
- **Actions do User:**
|
||||
- 📋 Copy — copia conteúdo
|
||||
- ✏️ Edit — abre o texto no input para edição + reenvio
|
||||
- 🗑️ Delete — remove mensagem
|
||||
|
||||
#### [MODIFY] `store/useAppStore.ts`
|
||||
- Adicionar `deleteMessage(id)`
|
||||
- Adicionar `editMessage(id, content)` — edita e reprocess
|
||||
- Adicionar `regenerateLastResponse()` — remove última resposta + reenvia
|
||||
|
||||
#### [MODIFY] `components/chat/ChatInput.tsx`
|
||||
- Suportar modo "edit" — pré-preencher input com texto da mensagem selecionada
|
||||
- Mostrar indicador visual de que está editando (ex: badge "Editing" + botão cancelar)
|
||||
|
||||
#### [NEW] `components/chat/MessageActions.tsx`
|
||||
- Componente de ações reutilizável
|
||||
- Botões com ícones (Lucide): `Copy`, `Pencil`, `RefreshCw`, `Trash2`
|
||||
- Touch targets 44px+ (Fitts' Law compliance)
|
||||
- Haptic feedback nos botões (`expo-haptics`)
|
||||
|
||||
---
|
||||
|
||||
### Fase 4: UI/UX Polish
|
||||
|
||||
#### [MODIFY] `components/chat/MessageBubble.tsx`
|
||||
- Avatar/ícone para assistant (Bot icon) e user
|
||||
- Timestamp discreto abaixo da bolha
|
||||
- Animação de entrada (fade in + slide up com `react-native-reanimated`)
|
||||
|
||||
#### [MODIFY] `app/chat/index.tsx`
|
||||
- Empty state quando não há mensagens (ícone + texto de boas-vindas + sugestões de prompts clicáveis)
|
||||
- Typing indicator animado (3 dots pulsando) enquanto `isStreaming` e conteúdo ainda vazio
|
||||
|
||||
#### [MODIFY] `components/chat/ChatInput.tsx`
|
||||
- Auto-grow do input (já faz multiline, melhorar visual)
|
||||
- Botão de "stop generation" (⏹️) visível durante streaming, substituindo o botão send
|
||||
- Contador de caracteres sutil para mensagens longas
|
||||
|
||||
#### [MODIFY] `app/chat/_layout.tsx`
|
||||
- Botão "New Chat" no header (limpa conversa)
|
||||
|
||||
---
|
||||
|
||||
## 💡 Sugestões Adicionais (Simples de Implementar)
|
||||
|
||||
1. **Sugestões de Prompts no Empty State** — 3-4 cards clicáveis com prompts exemplo ("Explique recursão", "Escreva um haiku") que preenchem o input ao tocar. Melhora onboarding e ajuda o user a começar.
|
||||
|
||||
2. **Persistência de Chat com AsyncStorage** — Salvar mensagens no dispositivo para não perder ao fechar o app. Usar `zustand/middleware` com `persist`.
|
||||
|
||||
3. **Haptic Feedback nas ações** — Vibração leve ao enviar mensagem, copiar, e deletar. Premium feel. (`expo-haptics` já está disponível no Expo).
|
||||
|
||||
4. **Scroll-to-bottom FAB** — Quando o user rola para cima e chega mensagem nova, mostrar um FAB "↓" no canto inferior para voltar ao final. Evita perder mensagens novas.
|
||||
|
||||
5. **Configuração de Temperature/Max Tokens no Chat** — Adicionar um menu de "Model Parameters" acessível pelo header (ícone de slider). Permite ajustar temperatura e max_tokens sem ir em Settings.
|
||||
|
||||
6. **Tema Dark Mode funcional** — O theme toggle existe mas as telas usam cores hardcoded (#FFFFFF, #F3F4F6). Conectar com o sistema `Colors` de `constants/theme.ts` para dark mode real.
|
||||
|
||||
7. **Indicador de tokens usados** — Mostrar contagem de tokens da resposta (retornado pelo `usage` da API) abaixo de cada mensagem assistant. Informativo para quem está testando modelos.
|
||||
|
||||
8. **System Prompt configurável** — Input nas Settings para definir o system prompt padrão que é enviado em toda conversa. Permite personalizar o "comportamento" do modelo.
|
||||
|
||||
---
|
||||
|
||||
## Verificação
|
||||
|
||||
1. Enviar mensagem → resposta deve aparecer via streaming (sem erro ReadableStream)
|
||||
2. Resposta do assistant → renderizada com formatação markdown (code blocks, bold, listas)
|
||||
3. Long-press em mensagem → botões de ação aparecem
|
||||
4. Copiar → conteúdo no clipboard
|
||||
5. Editar mensagem do user → input preenchido, reenvio funciona
|
||||
6. Empty state → sugestões de prompt visíveis
|
||||
7. Stop generation → streaming para imediatamente
|
||||
8. Dark mode → todas as telas respeitam o tema
|
||||
|
||||
---
|
||||
|
||||
## Ordem de Execução Recomendada
|
||||
|
||||
| Fase | Prioridade | Esforço |
|
||||
|------|-----------|---------|
|
||||
| 1. Fix Streaming | 🔴 Crítico | Baixo |
|
||||
| 2. Markdown | 🟠 Alto | Baixo |
|
||||
| 3. Message Actions | 🟡 Médio | Médio |
|
||||
| 4. UI/UX Polish | 🟢 Normal | Médio |
|
||||
| 5+ Sugestões | 🔵 Nice-to-have | Baixo cada |
|
||||
@@ -0,0 +1,96 @@
|
||||
# Plan: Models, Chat & Settings Screens
|
||||
|
||||
This document outlines the implementation plan for adding the Models, Chat, and Settings screens to the LocalAiApp, following the user's choice of **Option C** (Stack Navigation with Actions in Header) and targeting the Qwen 3.5 models (0.5b, 1.5b, 4b) via HuggingFace formats.
|
||||
The project is a **CROSS-PLATFORM** app built with Expo (iOS, Android, Web/Desktop).
|
||||
|
||||
## Overview
|
||||
We will implement a responsive, cross-platform Chat interface. On mobile, it will be a full-screen Chat interface with a top-bar header. On desktop/tablet breakpoints, the UI will adapt (e.g., potentially expanding the header actions or bringing settings into a side panel if needed, but primarily focusing on a fluid Chat layout that centers the conversation on wide screens). The application will manage model state through a central store, allowing quick selection from the header.
|
||||
|
||||
## Success Criteria
|
||||
* The `Chat` screen acts as the primary fluid view, centering the message thread on wide desktop displays max-width to avoid excessively long text lines, while extending full-width on mobile.
|
||||
* The models (Qwen 3.5 - 0.5b, 1.5b, 4b) can be selected via a dropdown or modal directly from the Chat header. On desktop, this might behave as a popover; on mobile, a bottom sheet.
|
||||
* A `Settings` screen is accessible via an icon in the Chat header. On mobile, it pushes onto the stack. On desktop, it may appear as a modal or dedicated full-page route optimized for mouse interaction.
|
||||
* A solid context/state management foundation (e.g., Zustand) is established to hold the current selected model and chat history across screens.
|
||||
* The UI must adhere to fluid design principles: larger touch targets for mobile (> 44pt), hover states and standard cursor interactions for desktop. No standard templates; use a custom, premium aesthetic.
|
||||
|
||||
## Tech Stack
|
||||
* **React Native / Expo Router**: For navigation and screen structure.
|
||||
* **Zustand**: For simple, performant global state management (models state, chat history).
|
||||
* **Vanilla Stylesheet / Tailwind (if configured)**: For custom, premium UI styling without generic templates.
|
||||
* **Lucide-React-Native / Expo Vector Icons**: For crisp, recognizable icons in the header and settings.
|
||||
* *(Future Integration)* `llama.rn` or similar: For actually executing the Qwen models locally on the device (Out of scope for this UI phase, but Settings UI must account for it).
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
├── app/
|
||||
│ ├── index.tsx # Main entry point (Redirects to chat or is the chat)
|
||||
│ ├── chat/
|
||||
│ │ ├── _layout.tsx # Defines the Stack Header (Model Selector + Settings Icon)
|
||||
│ │ ├── index.tsx # The Chat UI (Messages list, input area)
|
||||
│ ├── settings/
|
||||
│ │ ├── index.tsx # Settings Screen (Model management, keys, UI prefs)
|
||||
├── components/
|
||||
│ ├── chat/
|
||||
│ │ ├── MessageBubble.tsx # Defines how user/AI messages look
|
||||
│ │ ├── ChatInput.tsx # The text input area
|
||||
│ │ ├── ModelSelector.tsx # The dropdown/bottom sheet to pick Qwen models
|
||||
│ ├── ui/
|
||||
│ │ ├── IconButton.tsx # Reusable touch-friendly icon button
|
||||
├── store/
|
||||
│ ├── useAppStore.ts # Zustand store for active model, messages, and settings
|
||||
├── constants/
|
||||
│ ├── Models.ts # Definitions for Qwen 3.5 (0.5b, 1.5b, 4b) URLs/Configs
|
||||
```
|
||||
|
||||
## Task Breakdown
|
||||
|
||||
### 1- Setup Global State & Constants
|
||||
* **Agent:** `mobile-developer`
|
||||
* **Skills:** `clean-code`
|
||||
* **Task:** Create `Models.ts` with configurations for the selected Qwen 3.5 models. Setup a basic Zustand store `useAppStore.ts` to hold the `activeModel`, an array of `messages`, and user preferences.
|
||||
* **INPUT→OUTPUT→VERIFY:** Input: Empty files. Output: A working Zustand store and constant file. Verify: Can import store into a simple component and read/write values.
|
||||
|
||||
### 2- Implement Stack Navigation Layout & Responsive Shell
|
||||
* **Agent:** `frontend-specialist` (cross-platform expert)
|
||||
* **Skills:** `frontend-design`, `react-best-practices`
|
||||
* **Task:** Update `app/_layout.tsx` (or `app/chat/_layout.tsx`) to use Expo Router's `<Stack>` navigation. Implement a responsive container that limits max width on desktop sizes to ensure readability, while remaining 100% width on mobile. Configure the header of the `chat` screen to include the `ModelSelector` and Settings icon.
|
||||
* **INPUT→OUTPUT→VERIFY:** Input: Default Expo Router layout. Output: A cross-platform layout adapting to screen width (`useWindowDimensions`). Verify: Resize browser window (Web) to test breakpoints; launch mobile to confirm header structure.
|
||||
|
||||
### 3- Build Chat Components (Input & Bubbles)
|
||||
* **Agent:** `frontend-specialist`
|
||||
* **Skills:** `frontend-design`
|
||||
* **Task:** Create `components/chat/MessageBubble.tsx` and `components/chat/ChatInput.tsx`. These components must be responsive: message bubbles should have a max-width (e.g., 75% on mobile, 60% on desktop) and inputs should support Enter to send on Web (Shift+Enter for newline) alongside standard mobile keyboard behavior. Add hover states for desktop interactions.
|
||||
* **INPUT→OUTPUT→VERIFY:** Input: Layout skeleton. Output: Reusable chat components functioning on both touch and mouse interfaces. Verify: Test desktop hover states and Enter-key sending; test mobile touch targets and padding.
|
||||
|
||||
### 4- Build the Chat Screen (List & Integration)
|
||||
* **Agent:** `mobile-developer`
|
||||
* **Skills:** `performance-profiling`
|
||||
* **Task:** Implement `app/chat/index.tsx`. Use `FlatList` (or `FlashList` if available) to render the messages from the global state. Integrate the `ChatInput` at the bottom (handling Keyboard avoiding view correctly).
|
||||
* **INPUT→OUTPUT→VERIFY:** Input: Chat components. Output: A fully scrollable chat interface that reacts to keyboard input. Verify: Type a message, send it, verify it appears in the list and the list auto-scrolls.
|
||||
|
||||
### 5- Build Model Selector (Header Menu)
|
||||
* **Agent:** `mobile-developer`
|
||||
* **Skills:** `mobile-design`
|
||||
* **Task:** Implement `components/chat/ModelSelector.tsx`. This should be a touchable area in the header that opens a bottom sheet or a contextual menu to switch between the Qwen 3.5 models (0.5b, 1.5b, 4b).
|
||||
* **INPUT→OUTPUT→VERIFY:** Input: Header layout. Output: Interactive menu for model selection. Verify: Tapping the header opens the menu, selecting a model updates the global state.
|
||||
|
||||
### 6- Build Settings Screen
|
||||
* **Agent:** `frontend-specialist`
|
||||
* **Skills:** `frontend-design`
|
||||
* **Task:** Implement `app/settings/index.tsx`. Group settings into logical sections. On wide screens, this could be a centered card layout; on mobile, a standard list. Add toggles/inputs for configuration with proper focus states for keyboard navigation on Web/Desktop.
|
||||
* **INPUT→OUTPUT→VERIFY:** Input: Navigation setup. Output: A functional, responsive settings screen. Verify: Navigate to Settings, change sizes from mobile to desktop, check alignment and focus rings.
|
||||
|
||||
## Phase X: Verification Plan
|
||||
|
||||
To verify this cross-platform implementation, the following checklist must be executed once all tasks are complete:
|
||||
|
||||
- [ ] **Linting & Types:** Run `npm run lint` and `npx tsc --noEmit`. Fix any warnings/errors.
|
||||
- [ ] **Cross-Platform Accessibility:** Audit touch targets for mobile (> 44pt) and verify hover/focus states are present for Desktop users (mouse/keyboard).
|
||||
- [ ] **Responsive Resizing:** Run `npm run web` and resize the browser horizontally. Ensure text lines in chat don't stretch indefinitely and the input field remains centered/bounded.
|
||||
- [ ] **Design Constraints Check:** Verify NO purple/violet `#8A2BE2` are used. Ensure UI feels premium on both small and large screens.
|
||||
- [ ] **Keyboard Handling:**
|
||||
- *Mobile:* Ensure Keyboard Avoiding View prevents covering the input.
|
||||
- *Desktop:* Ensure hitting `Enter` sends the message, and `Shift+Enter` adds a newline.
|
||||
- [ ] **State Persistence (Basic):** Select a model -> Go to Settings -> Change a setting -> Go back to Chat -> Verify the selected model hasn't reset.
|
||||
- [ ] **Build:** Run `npm run android`, `npm run ios`, and `npm run web` successfully without bundler errors.
|
||||
@@ -0,0 +1,491 @@
|
||||
# Documentação Técnica Completa — LM Studio API
|
||||
|
||||
> Baseada na documentação oficial em `lmstudio.ai/docs/developer` [lmstudio](https://lmstudio.ai/docs/developer)
|
||||
|
||||
***
|
||||
|
||||
## Visão Geral
|
||||
|
||||
O LM Studio expõe APIs locais para inferência de LLMs com suporte a múltiplos protocolos: [lmstudio](https://lmstudio.ai/docs/developer)
|
||||
|
||||
- **REST API nativa** (`/api/v0`) — stateful, com stats avançados
|
||||
- **OpenAI-compatible** (`/v1`) — drop-in replacement para projetos existentes
|
||||
- **Anthropic-compatible** — endpoint Messages
|
||||
- **SDKs oficiais** — `lmstudio-js` (TypeScript) e `lmstudio-python`
|
||||
- **CLI** — `lms` para controle headless
|
||||
|
||||
O servidor roda por padrão em `http://localhost:1234`. [lmstudio](https://lmstudio.ai/docs/developer/rest/endpoints)
|
||||
|
||||
***
|
||||
|
||||
## Inicialização do Servidor
|
||||
|
||||
### Via App (GUI)
|
||||
Acesse a aba **"Local Server"** no LM Studio e clique em **Start Server**.
|
||||
|
||||
### Via CLI (Headless)
|
||||
```bash
|
||||
lms server start
|
||||
lms server stop
|
||||
lms server status
|
||||
```
|
||||
|
||||
Para uso em Linux como startup task (systemd), o LM Studio oferece suporte nativo a **Headless Mode**. [lmstudio](https://lmstudio.ai/docs/developer)
|
||||
|
||||
***
|
||||
|
||||
## Autenticação
|
||||
|
||||
A API suporta token Bearer opcional: [lmstudio](https://lmstudio.ai/docs/developer/rest/endpoints)
|
||||
|
||||
```bash
|
||||
# Definir token via variável de ambiente
|
||||
export LM_API_TOKEN="seu-token-aqui"
|
||||
|
||||
# Usar no header
|
||||
curl http://localhost:1234/api/v0/models \
|
||||
-H "Authorization: Bearer $LM_API_TOKEN"
|
||||
```
|
||||
|
||||
Por padrão, a API key pode ser vazia/omitida em ambiente local. [docs.litellm](https://docs.litellm.ai/docs/providers/lm_studio)
|
||||
|
||||
***
|
||||
|
||||
## REST API v0 (Nativa)
|
||||
|
||||
### Base URL
|
||||
```
|
||||
http://localhost:1234/api/v0
|
||||
```
|
||||
|
||||
### Endpoints disponíveis [lmstudio](https://lmstudio.ai/docs/developer/rest/endpoints)
|
||||
|
||||
| Método | Endpoint | Descrição |
|
||||
|--------|----------|-----------|
|
||||
| `GET` | `/api/v0/models` | Lista todos os modelos disponíveis |
|
||||
| `GET` | `/api/v0/models/{model}` | Info de um modelo específico |
|
||||
| `POST` | `/api/v0/chat/completions` | Chat com histórico de mensagens |
|
||||
| `POST` | `/api/v0/completions` | Text completion (prompt → texto) |
|
||||
| `POST` | `/api/v0/embeddings` | Geração de embeddings |
|
||||
|
||||
Diferencial desta API: inclui métricas como **tokens/segundo** e **TTFT (Time to First Token)**. [lmstudio](https://lmstudio.ai/docs/developer/rest/endpoints)
|
||||
|
||||
***
|
||||
|
||||
### GET /api/v0/models
|
||||
|
||||
```bash
|
||||
curl http://localhost:1234/api/v0/models \
|
||||
-H "Authorization: Bearer $LM_API_TOKEN"
|
||||
```
|
||||
|
||||
**Resposta:**
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "granite-3.0-2b-instruct",
|
||||
"object": "model",
|
||||
"owned_by": "lmstudio"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
### POST /api/v0/chat/completions
|
||||
|
||||
```bash
|
||||
curl http://localhost:1234/api/v0/chat/completions \
|
||||
-H "Authorization: Bearer $LM_API_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "granite-3.0-2b-instruct",
|
||||
"messages": [
|
||||
{ "role": "system", "content": "Você é um assistente útil." },
|
||||
{ "role": "user", "content": "Explique recursão." }
|
||||
],
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 512,
|
||||
"stream": false
|
||||
}'
|
||||
```
|
||||
|
||||
**Parâmetros principais:**
|
||||
|
||||
| Parâmetro | Tipo | Descrição |
|
||||
|-----------|------|-----------|
|
||||
| `model` | `string` | ID do modelo carregado |
|
||||
| `messages` | `array` | Histórico de mensagens (role + content) |
|
||||
| `temperature` | `float` | Criatividade (0.0–1.0) |
|
||||
| `max_tokens` | `int` | Limite de tokens na resposta |
|
||||
| `stream` | `bool` | Streaming SSE (true/false) |
|
||||
| `stop` | `string\|array` | Token(s) de parada |
|
||||
| `top_p` | `float` | Nucleus sampling |
|
||||
| `frequency_penalty` | `float` | Penalidade de repetição |
|
||||
|
||||
***
|
||||
|
||||
### POST /api/v0/completions (Legacy/Text)
|
||||
|
||||
```bash
|
||||
curl http://localhost:1234/api/v0/completions \
|
||||
-H "Authorization: Bearer $LM_API_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "granite-3.0-2b-instruct",
|
||||
"prompt": "O sentido da vida é",
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 10,
|
||||
"stream": false,
|
||||
"stop": "\n"
|
||||
}'
|
||||
```
|
||||
|
||||
|
||||
|
||||
***
|
||||
|
||||
### POST /api/v0/embeddings
|
||||
|
||||
```bash
|
||||
curl http://localhost:1234/api/v0/embeddings \
|
||||
-H "Authorization: Bearer $LM_API_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "text-embedding-model",
|
||||
"input": "Texto para vetorizar"
|
||||
}'
|
||||
```
|
||||
|
||||
**Resposta:**
|
||||
```json
|
||||
{
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"object": "embedding",
|
||||
"embedding": [0.0023, -0.0142, ...],
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Streaming (SSE)
|
||||
|
||||
Quando `"stream": true`, a resposta retorna eventos Server-Sent Events (SSE): [lmstudio](https://lmstudio.ai/docs/developer)
|
||||
|
||||
```python
|
||||
import httpx, json
|
||||
|
||||
url = "http://localhost:1234/api/v0/chat/completions"
|
||||
payload = {
|
||||
"model": "granite-3.0-2b-instruct",
|
||||
"messages": [{"role": "user", "content": "Olá!"}],
|
||||
"stream": True
|
||||
}
|
||||
|
||||
with httpx.stream("POST", url, json=payload) as r:
|
||||
for line in r.iter_lines():
|
||||
if line.startswith("data: "):
|
||||
chunk = line[6:]
|
||||
if chunk == "[DONE]":
|
||||
break
|
||||
data = json.loads(chunk)
|
||||
print(data["choices"][0]["delta"].get("content", ""), end="")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## OpenAI-Compatible Endpoints (`/v1`)
|
||||
|
||||
### Base URL
|
||||
```
|
||||
http://localhost:1234/v1
|
||||
```
|
||||
|
||||
### Endpoints disponíveis [lmstudio](https://lmstudio.ai/docs/developer)
|
||||
|
||||
| Método | Endpoint | Descrição |
|
||||
|--------|----------|-----------|
|
||||
| `GET` | `/v1/models` | Lista modelos |
|
||||
| `POST` | `/v1/chat/completions` | Chat completions |
|
||||
| `POST` | `/v1/completions` | Text completions (legacy) |
|
||||
| `POST` | `/v1/embeddings` | Embeddings |
|
||||
| `POST` | `/v1/responses` | Responses API (novo) |
|
||||
|
||||
### Uso com SDK OpenAI (Python)
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI(
|
||||
base_url="http://localhost:1234/v1",
|
||||
api_key="lm-studio" # qualquer string não-vazia
|
||||
)
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="granite-3.0-2b-instruct",
|
||||
messages=[
|
||||
{"role": "system", "content": "Você é um assistente técnico."},
|
||||
{"role": "user", "content": "O que é Big O notation?"}
|
||||
],
|
||||
temperature=0.3
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
### Uso com SDK OpenAI (Node.js/TypeScript)
|
||||
|
||||
```typescript
|
||||
import OpenAI from "openai";
|
||||
|
||||
const client = new OpenAI({
|
||||
baseURL: "http://localhost:1234/v1",
|
||||
apiKey: "lm-studio",
|
||||
});
|
||||
|
||||
const response = await client.chat.completions.create({
|
||||
model: "granite-3.0-2b-instruct",
|
||||
messages: [{ role: "user", content: "Explique ponteiros em C." }],
|
||||
temperature: 0.3,
|
||||
});
|
||||
|
||||
console.log(response.choices[0].message.content);
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Anthropic-Compatible Endpoint
|
||||
|
||||
### Base URL
|
||||
```
|
||||
http://localhost:1234/anthropic
|
||||
```
|
||||
|
||||
### POST /anthropic/v1/messages [lmstudio](https://lmstudio.ai/docs/developer)
|
||||
|
||||
```python
|
||||
import anthropic
|
||||
|
||||
client = anthropic.Anthropic(
|
||||
base_url="http://localhost:1234/anthropic",
|
||||
api_key="lm-studio"
|
||||
)
|
||||
|
||||
message = client.messages.create(
|
||||
model="seu-modelo-local",
|
||||
max_tokens=1024,
|
||||
messages=[{"role": "user", "content": "Olá!"}]
|
||||
)
|
||||
print(message.content)
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Structured Output (JSON Schema)
|
||||
|
||||
O LM Studio suporta saída estruturada via `response_format`: [lmstudio](https://lmstudio.ai/docs/developer)
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
import json
|
||||
|
||||
client = OpenAI(base_url="http://localhost:1234/v1", api_key="lm-studio")
|
||||
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"nome": {"type": "string"},
|
||||
"linguagem": {"type": "string"},
|
||||
"complexidade": {"type": "string", "enum": ["baixa", "média", "alta"]}
|
||||
},
|
||||
"required": ["nome", "linguagem", "complexidade"]
|
||||
}
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="granite-3.0-2b-instruct",
|
||||
messages=[{"role": "user", "content": "Descreva o algoritmo quicksort."}],
|
||||
response_format={
|
||||
"type": "json_schema",
|
||||
"json_schema": {
|
||||
"name": "algoritmo",
|
||||
"schema": schema,
|
||||
"strict": True
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
data = json.loads(response.choices[0].message.content)
|
||||
print(data)
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Tool Calling / Function Calling
|
||||
|
||||
Suporte a agentes com chamada de ferramentas: [lmstudio](https://lmstudio.ai/docs/developer)
|
||||
|
||||
```python
|
||||
tools = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "buscar_temperatura",
|
||||
"description": "Retorna temperatura de uma cidade",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"cidade": {"type": "string", "description": "Nome da cidade"}
|
||||
},
|
||||
"required": ["cidade"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="granite-3.0-2b-instruct",
|
||||
messages=[{"role": "user", "content": "Qual a temperatura em Garuva?"}],
|
||||
tools=tools,
|
||||
tool_choice="auto"
|
||||
)
|
||||
|
||||
tool_call = response.choices[0].message.tool_calls[0]
|
||||
print(tool_call.function.name) # buscar_temperatura
|
||||
print(tool_call.function.arguments) # {"cidade": "Garuva"}
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Gerenciamento de Modelos via API
|
||||
|
||||
Além de servir inferência, a API nativa permite gerenciar modelos programaticamente: [lmstudio](https://lmstudio.ai/docs/developer)
|
||||
|
||||
| Ação | Método | Endpoint |
|
||||
|------|--------|----------|
|
||||
| Listar modelos | `GET` | `/api/v0/models` |
|
||||
| Carregar modelo | `POST` | `/api/v0/models/load` |
|
||||
| Descarregar modelo | `POST` | `/api/v0/models/unload` |
|
||||
| Baixar modelo | `POST` | `/api/v0/models/download` |
|
||||
| Status do download | `GET` | `/api/v0/models/download/status` |
|
||||
|
||||
```bash
|
||||
# Carregar modelo
|
||||
curl -X POST http://localhost:1234/api/v0/models/load \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"model": "granite-3.0-2b-instruct"}'
|
||||
|
||||
# Descarregar modelo
|
||||
curl -X POST http://localhost:1234/api/v0/models/unload \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"model": "granite-3.0-2b-instruct"}'
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## MCP (Model Context Protocol) via API
|
||||
|
||||
O LM Studio suporta MCP servers conectados via API, permitindo agentes com acesso a ferramentas externas (filesystem, banco de dados, etc.): [lmstudio](https://lmstudio.ai/docs/developer)
|
||||
|
||||
```typescript
|
||||
// lmstudio-js SDK com MCP
|
||||
import { LMStudioClient } from "@lmstudio/sdk";
|
||||
|
||||
const client = new LMStudioClient();
|
||||
// Conectar ao MCP server e associar ao modelo carregado
|
||||
```
|
||||
|
||||
> Para detalhes completos de MCP, consulte `lmstudio.ai/docs/developer` → **Using MCP via API**. [lmstudio](https://lmstudio.ai/docs/developer)
|
||||
|
||||
***
|
||||
|
||||
## SDK Nativo: lmstudio-python
|
||||
|
||||
```bash
|
||||
pip install lmstudio
|
||||
```
|
||||
|
||||
```python
|
||||
import lmstudio
|
||||
|
||||
client = lmstudio.Client() # conecta em localhost:1234 por padrão
|
||||
|
||||
# Chat simples
|
||||
response = client.llm.respond(
|
||||
"granite-3.0-2b-instruct",
|
||||
[{"role": "user", "content": "O que é Docker?"}]
|
||||
)
|
||||
print(response.content)
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## SDK Nativo: lmstudio-js
|
||||
|
||||
```bash
|
||||
npm install @lmstudio/sdk
|
||||
```
|
||||
|
||||
```typescript
|
||||
import { LMStudioClient } from "@lmstudio/sdk";
|
||||
|
||||
const client = new LMStudioClient();
|
||||
const model = await client.llm.load("granite-3.0-2b-instruct");
|
||||
|
||||
const response = await model.respond([
|
||||
{ role: "user", content: "O que é recursão de cauda?" }
|
||||
]);
|
||||
|
||||
console.log(response.content);
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Idle TTL e Auto-Evict
|
||||
|
||||
O LM Studio suporta configuração de **TTL por modelo** para liberar VRAM automaticamente quando inativo: [lmstudio](https://lmstudio.ai/docs/developer)
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "granite-3.0-2b-instruct",
|
||||
"ttl": 300
|
||||
}
|
||||
```
|
||||
|
||||
- `ttl`: tempo em segundos até o modelo ser descarregado automaticamente
|
||||
- Útil em ambientes com múltiplos modelos e VRAM limitada
|
||||
|
||||
***
|
||||
|
||||
## Chats Stateful (Stateful Chats)
|
||||
|
||||
A API nativa `/api/v0` suporta sessões com histórico gerenciado pelo servidor, diferente do padrão OpenAI onde o cliente envia o histórico completo a cada requisição. [lmstudio](https://lmstudio.ai/docs/developer)
|
||||
|
||||
> Consulte `lmstudio.ai/docs/developer/rest` → **Stateful Chats** para o contrato completo do endpoint. [lmstudio](https://lmstudio.ai/docs/developer/rest)
|
||||
|
||||
***
|
||||
|
||||
## Referência Rápida de Portas e URLs
|
||||
|
||||
| Endpoint | URL |
|
||||
|----------|-----|
|
||||
| REST API nativa | `http://localhost:1234/api/v0` |
|
||||
| OpenAI-compat | `http://localhost:1234/v1` |
|
||||
| Anthropic-compat | `http://localhost:1234/anthropic` |
|
||||
| Porta padrão | `1234` (configurável) |
|
||||
|
||||
***
|
||||
|
||||
## Trade-offs e Considerações
|
||||
|
||||
- **REST v0 nativa** → use quando precisar de métricas (TTFT, tokens/s), gerenciamento de modelos ou chats stateful [lmstudio](https://lmstudio.ai/docs/developer/rest/endpoints)
|
||||
- **`/v1` OpenAI-compat** → use para máxima portabilidade e compatibilidade com SDKs existentes [lmstudio](https://lmstudio.ai/docs/developer)
|
||||
- **Anthropic-compat** → use se seu projeto já usa o SDK `anthropic` [lmstudio](https://lmstudio.ai/docs/developer)
|
||||
- **lmstudio-js / lmstudio-python** → use para acesso a features exclusivas como MCP e controle granular de modelos [lmstudio](https://lmstudio.ai/docs/developer)
|
||||
- **LM Studio não é licenciado para uso comercial** — verifique os termos antes de deploy em produção [youtube](https://www.youtube.com/watch?v=Flz2oy8D5Uw)
|
||||
Reference in new issue
Block a user