jeanbaptdzd commited on
Commit
6d3bf74
·
1 Parent(s): c77ec91

Reorganize tests and clean up documentation

Browse files

- Move integration tests to tests/integration/ directory
- Add integration tests documentation in README
- Remove outdated docs (tool_calls_analysis, reasoning_models)
- Clean up remaining docs: remove PydanticAI references
- Fix unit tests (temperature default, conftest cleanup)
- Update project structure documentation

README.md CHANGED
@@ -19,14 +19,14 @@ This service provides an OpenAI-compatible API for the DragonLLM Qwen3-8B financ
19
 
20
  ## Features
21
 
22
- - ✅ **OpenAI-Compatible API** - Drop-in replacement for OpenAI API
23
- - ✅ **French & English Support** - Automatic language detection
24
- - ✅ **Rate Limiting** - Built-in protection (30 req/min, 500 req/hour)
25
- - ✅ **Statistics Tracking** - Token usage and request metrics via `/v1/stats`
26
- - ✅ **Health Monitoring** - Model readiness status in `/health` endpoint
27
- - ✅ **Streaming Support** - Real-time response streaming
28
- - ✅ **Tool Calls Support** - OpenAI-compatible tool/function calling
29
- - ✅ **Structured Outputs** - JSON format support via response_format
30
 
31
  ## API Endpoints
32
 
@@ -133,7 +133,7 @@ lm = dspy.OpenAI(
133
  - English and French support
134
 
135
  **Backend:**
136
- - Transformers 4.40.0+
137
  - PyTorch 2.5.0+ (CUDA 12.4)
138
  - Accelerate 0.30.0+
139
 
@@ -157,14 +157,33 @@ uvicorn app.main:app --reload --port 8080
157
 
158
  ### Testing
159
 
 
160
  ```bash
161
- # Run tests
162
- pytest -v
 
 
 
 
 
 
 
163
 
164
- # Test deployment
165
- ./test_deployment.sh
 
 
 
166
  ```
167
 
 
 
 
 
 
 
 
 
168
  ## Project Structure
169
 
170
  ```
@@ -177,6 +196,8 @@ pytest -v
177
  │ └── utils/ # Utilities, stats tracking
178
  ├── docs/ # Documentation
179
  ├── tests/ # Test suite
 
 
180
  └── scripts/ # Utility scripts
181
  ```
182
 
 
19
 
20
  ## Features
21
 
22
+ - OpenAI-compatible API - Drop-in replacement for OpenAI API
23
+ - French and English support - Automatic language detection
24
+ - Rate limiting - Built-in protection (30 req/min, 500 req/hour)
25
+ - Statistics tracking - Token usage and request metrics via `/v1/stats`
26
+ - Health monitoring - Model readiness status in `/health` endpoint
27
+ - Streaming support - Real-time response streaming
28
+ - Tool calls support - OpenAI-compatible tool/function calling
29
+ - Structured outputs - JSON format support via response_format
30
 
31
  ## API Endpoints
32
 
 
133
  - English and French support
134
 
135
  **Backend:**
136
+ - Transformers 4.45.0+
137
  - PyTorch 2.5.0+ (CUDA 12.4)
138
  - Accelerate 0.30.0+
139
 
 
157
 
158
  ### Testing
159
 
160
+ **Unit Tests:**
161
  ```bash
162
+ pytest tests/ -v
163
+ ```
164
+
165
+ **Integration Tests:**
166
+ The integration tests evaluate the model's ability to produce valid JSON outputs and execute tool calls, which are critical requirements for financial applications.
167
+
168
+ ```bash
169
+ # Basic API functionality
170
+ python tests/integration/test_space_basic.py
171
 
172
+ # Tool calls and JSON format
173
+ python tests/integration/test_space_with_tools.py
174
+
175
+ # Detailed tool call validation
176
+ python tests/integration/test_tool_calls.py
177
  ```
178
 
179
+ **Test Coverage:**
180
+ - API endpoints (health, models, chat completions)
181
+ - Tool calls with `tool_choice` parameter
182
+ - Structured JSON outputs via `response_format`
183
+ - Model response parsing and validation
184
+
185
+ These tests verify that the small 8B model can reliably produce valid JSON and execute tool calls, which is mandatory for financial workflows requiring structured data and function execution.
186
+
187
  ## Project Structure
188
 
189
  ```
 
196
  │ └── utils/ # Utilities, stats tracking
197
  ├── docs/ # Documentation
198
  ├── tests/ # Test suite
199
+ │ ├── integration/ # Integration tests (API, tool calls, JSON)
200
+ │ └── performance/ # Performance benchmarks
201
  └── scripts/ # Utility scripts
202
  ```
203
 
docs/openai_api_verification.md CHANGED
@@ -6,8 +6,8 @@ This document verifies that our OpenAI API wrapper implementation correctly foll
6
  ## Connection Flow
7
 
8
  ```
9
- PydanticAI Agent
10
- ↓ (OpenAI-compatible requests)
11
  Hugging Face Space API (simple-llm-pro-finance)
12
  ↓ (FastAPI router)
13
  TransformersProvider
@@ -93,30 +93,29 @@ Qwen-Open-Finance-R-8B Model
93
  When `response_format={"type": "json_object"}` is provided:
94
  - ✅ System prompt is enhanced with JSON output instructions
95
  - ✅ Response is parsed to extract JSON from markdown code blocks
96
- - ✅ Clean JSON is returned for PydanticAI validation
97
 
98
  **Implementation**: Since Qwen doesn't have native JSON mode, we enforce it via prompt engineering and post-processing.
99
 
100
- ## PydanticAI Integration
101
 
102
- ### ✅ What PydanticAI Sends
103
 
104
- When using `output_type` parameter:
105
 
106
  ```python
107
- # PydanticAI sends:
108
  {
109
  "model": "dragon-llm-open-finance",
110
  "messages": [...],
111
  "temperature": 0.7,
112
  "max_tokens": 3000,
113
- "response_format": {"type": "json_object"}, # ✅ Now supported
114
- "tool_choice": "required", # ✅ Now accepted (converted to "auto")
115
- "tools": [...] # ✅ If tools are defined
116
  }
117
  ```
118
 
119
- ### ✅ Our Implementation Handles
120
 
121
  1. ✅ `tool_choice="required"` → Accepted and converted to `"auto"`
122
  2. ✅ `response_format={"type": "json_object"}` → JSON instructions added to prompt
@@ -163,10 +162,10 @@ When using `output_type` parameter:
163
  - [x] Streaming support implemented
164
  - [x] Tool calls properly formatted
165
 
166
- ### PydanticAI Compatibility
167
  - [x] `tool_choice="required"` accepted
168
  - [x] `response_format` supported
169
- - [x] `output_type` requests handled correctly
170
  - [x] Tool definitions passed through
171
  - [x] Structured outputs extracted
172
 
@@ -181,7 +180,7 @@ When using `output_type` parameter:
181
 
182
  1. **Basic Chat**: Verify simple chat completions work
183
  2. **Tool Calls**: Test with tools defined, verify parsing
184
- 3. **Structured Outputs**: Test with `output_type`, verify JSON extraction
185
  4. **Error Handling**: Test invalid requests return proper errors
186
  5. **Streaming**: Test streaming responses work correctly
187
 
@@ -197,7 +196,7 @@ When using `output_type` parameter:
197
 
198
  The implementation:
199
  - Follows OpenAI API specification
200
- - Handles PydanticAI-specific parameters correctly
201
  - Properly integrates with Qwen model via Transformers
202
  - Provides fallbacks for features not natively supported by Qwen
203
 
 
6
  ## Connection Flow
7
 
8
  ```
9
+ OpenAI-compatible Client
10
+ ↓ (OpenAI API requests)
11
  Hugging Face Space API (simple-llm-pro-finance)
12
  ↓ (FastAPI router)
13
  TransformersProvider
 
93
  When `response_format={"type": "json_object"}` is provided:
94
  - ✅ System prompt is enhanced with JSON output instructions
95
  - ✅ Response is parsed to extract JSON from markdown code blocks
96
+ - ✅ Clean JSON is returned for validation
97
 
98
  **Implementation**: Since Qwen doesn't have native JSON mode, we enforce it via prompt engineering and post-processing.
99
 
100
+ ## Client Integration
101
 
102
+ ### ✅ Supported Parameters
103
 
104
+ The API accepts standard OpenAI API parameters:
105
 
106
  ```python
 
107
  {
108
  "model": "dragon-llm-open-finance",
109
  "messages": [...],
110
  "temperature": 0.7,
111
  "max_tokens": 3000,
112
+ "response_format": {"type": "json_object"}, # ✅ Supported
113
+ "tool_choice": "required", # ✅ Accepted (converted to "auto")
114
+ "tools": [...] # ✅ Tool definitions supported
115
  }
116
  ```
117
 
118
+ ### ✅ Implementation Details
119
 
120
  1. ✅ `tool_choice="required"` → Accepted and converted to `"auto"`
121
  2. ✅ `response_format={"type": "json_object"}` → JSON instructions added to prompt
 
162
  - [x] Streaming support implemented
163
  - [x] Tool calls properly formatted
164
 
165
+ ### Client Compatibility
166
  - [x] `tool_choice="required"` accepted
167
  - [x] `response_format` supported
168
+ - [x] Structured output requests handled correctly
169
  - [x] Tool definitions passed through
170
  - [x] Structured outputs extracted
171
 
 
180
 
181
  1. **Basic Chat**: Verify simple chat completions work
182
  2. **Tool Calls**: Test with tools defined, verify parsing
183
+ 3. **Structured Outputs**: Test with `response_format`, verify JSON extraction
184
  4. **Error Handling**: Test invalid requests return proper errors
185
  5. **Streaming**: Test streaming responses work correctly
186
 
 
196
 
197
  The implementation:
198
  - Follows OpenAI API specification
199
+ - Handles OpenAI-compatible parameters correctly
200
  - Properly integrates with Qwen model via Transformers
201
  - Provides fallbacks for features not natively supported by Qwen
202
 
docs/qwen3_specifications.md CHANGED
@@ -45,8 +45,8 @@ Contexte total = Prompt système + Messages conversation + Réponse générée
45
 
46
  ## Configuration actuelle
47
 
48
- Dans notre application PydanticAI:
49
- - `max_tokens` (génération): **1500 tokens** (configurable)
50
  - Contexte d'entrée: Illimité jusqu'à ~30K tokens (pour laisser de la marge)
51
  - Contexte total: Jusqu'à 32K tokens (base) ou 128K (avec YaRN)
52
  - Limite théorique max: 20K tokens en sortie (mais contrainte par contexte disponible)
 
45
 
46
  ## Configuration actuelle
47
 
48
+ Dans notre application:
49
+ - `max_tokens` (génération): **1500 tokens** (configurable via API)
50
  - Contexte d'entrée: Illimité jusqu'à ~30K tokens (pour laisser de la marge)
51
  - Contexte total: Jusqu'à 32K tokens (base) ou 128K (avec YaRN)
52
  - Limite théorique max: 20K tokens en sortie (mais contrainte par contexte disponible)
docs/reasoning_models.md DELETED
@@ -1,94 +0,0 @@
1
- # Gestion des modèles de raisonnement avec PydanticAI
2
-
3
- ## Problème: "finish on length"
4
-
5
- Quand vous voyez `finish_reason: "length"`, cela signifie que le modèle a atteint la limite de `max_tokens` avant de terminer sa réponse.
6
-
7
- ## Pourquoi c'est fréquent avec les modèles de raisonnement?
8
-
9
- Les modèles comme Qwen3 utilisent des balises `<think>` (ou `<think>`) pour le raisonnement en chaîne:
10
-
11
- ```
12
- <think>
13
- 1. L'utilisateur demande un message SWIFT MT103
14
- 2. Je dois identifier les champs requis
15
- 3. Format: :20: référence, :32A: date/devise/montant...
16
- </think>
17
-
18
- Voici le message SWIFT généré:
19
- :20:NONREF
20
- :23B:CRED
21
- ...
22
- ```
23
-
24
- **Le raisonnement peut consommer 40-60% du budget de tokens!**
25
-
26
- ## Solution: Augmenter max_tokens
27
-
28
- Nous avons configuré `max_tokens=1500` dans `app/config.py` pour permettre:
29
- - ~600-900 tokens pour le raisonnement (`<think>` tags)
30
- - ~600-900 tokens pour la réponse finale
31
- - Total: ~1500 tokens pour des réponses complètes
32
-
33
- ## Configuration actuelle
34
-
35
- ```python
36
- # app/config.py
37
- max_tokens: int = 1500 # Pour modèles de raisonnement
38
-
39
- # app/models.py
40
- model_settings = ModelSettings(
41
- max_output_tokens=settings.max_tokens,
42
- )
43
- finance_model = OpenAIModel(
44
- ...,
45
- model_settings=model_settings,
46
- )
47
- ```
48
-
49
- ## Recommandations par type de requête
50
-
51
- | Type de requête | max_tokens recommandé |
52
- |----------------|----------------------|
53
- | Questions simples | 800-1000 |
54
- | Génération SWIFT | 1200-1500 |
55
- | Analyse complexe | 1500-2000 |
56
- | Extraction structurée | 1000-1200 |
57
-
58
- ## Comment ajuster pour un agent spécifique?
59
-
60
- Vous pouvez créer des agents avec des settings différents:
61
-
62
- ```python
63
- from pydantic_ai import ModelSettings, Agent
64
-
65
- # Agent pour tâches courtes
66
- short_agent = Agent(
67
- finance_model,
68
- model_settings=ModelSettings(max_output_tokens=800),
69
- system_prompt="..."
70
- )
71
-
72
- # Agent pour tâches longues (SWIFT, analyses)
73
- long_agent = Agent(
74
- finance_model,
75
- model_settings=ModelSettings(max_output_tokens=2000),
76
- system_prompt="..."
77
- )
78
- ```
79
-
80
- ## Vérifier si la réponse est complète
81
-
82
- Notre utilitaire `extract_answer_from_reasoning()` dans `app/utils.py` gère automatiquement:
83
- - Extraction de la réponse après les balises `<think>`
84
- - Détection si la réponse est tronquée
85
- - Nettoyage des balises de raisonnement
86
-
87
-
88
-
89
-
90
-
91
-
92
-
93
-
94
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
docs/tool_calls_analysis_hf_space.md DELETED
@@ -1,257 +0,0 @@
1
- # Analyse : Pourquoi les Tool Calls ne Fonctionnent Pas
2
-
3
- ## 🔍 Problème Identifié
4
-
5
- L'API Hugging Face Space **ne supporte PAS les tool calls** dans son implémentation actuelle.
6
-
7
- ## 📋 Analyse du Code
8
-
9
- ### 1. Modèle de Requête (`app/models/openai.py`)
10
-
11
- ```python
12
- class ChatCompletionRequest(BaseModel):
13
- model: Optional[str] = None
14
- messages: List[Message]
15
- temperature: Optional[float] = 0.7
16
- max_tokens: Optional[int] = None
17
- stream: Optional[bool] = False
18
- top_p: Optional[float] = 1.0
19
- # ❌ PAS de champ "tools"
20
- # ❌ PAS de champ "tool_choice"
21
- ```
22
-
23
- **Problème :** Le modèle Pydantic ne définit pas les champs `tools` et `tool_choice`, donc même si PydanticAI les envoie, ils sont **ignorés** par FastAPI.
24
-
25
- ### 2. Modèle de Réponse (`app/models/openai.py`)
26
-
27
- ```python
28
- class ChoiceMessage(BaseModel):
29
- role: Literal["assistant"]
30
- content: Optional[str] = None
31
- # ❌ PAS de champ "tool_calls"
32
- ```
33
-
34
- **Problème :** Le modèle de réponse ne définit pas le champ `tool_calls`, donc même si le modèle générait des tool calls, ils ne seraient **pas retournés** dans la réponse.
35
-
36
- ### 3. Provider Transformers (`app/providers/transformers_provider.py`)
37
-
38
- ```python
39
- async def chat(self, payload: Dict[str, Any], stream: bool = False):
40
- messages = payload.get("messages", [])
41
- temperature = payload.get("temperature", DEFAULT_TEMPERATURE)
42
- max_tokens = payload.get("max_tokens", DEFAULT_MAX_TOKENS)
43
- top_p = payload.get("top_p", DEFAULT_TOP_P)
44
- # ❌ PAS d'extraction de "tools"
45
- # ❌ PAS d'extraction de "tool_choice"
46
-
47
- # Génère juste du texte
48
- generated_text = tokenizer.decode(generated_ids, skip_special_tokens=True)
49
-
50
- return {
51
- "choices": [{
52
- "message": {"role": "assistant", "content": generated_text},
53
- # ❌ PAS de "tool_calls"
54
- }]
55
- }
56
- ```
57
-
58
- **Problème :** Le provider :
59
- 1. N'extrait pas `tools` du payload
60
- 2. Ne passe pas les tools au modèle
61
- 3. Ne parse pas les tool calls de la réponse
62
- 4. Ne retourne pas de `tool_calls` dans la réponse
63
-
64
- ## 🔄 Flux Actuel
65
-
66
- ```
67
- PydanticAI Agent
68
- ↓ (envoie tools dans la requête)
69
- FastAPI Router
70
- ↓ (parse avec ChatCompletionRequest - IGNORE tools)
71
- TransformersProvider
72
- ↓ (n'extrait pas tools du payload)
73
- Qwen 8B Model
74
- ↓ (génère du texte, pas de tool calls)
75
- TransformersProvider
76
- ↓ (retourne juste content, pas tool_calls)
77
- FastAPI Router
78
- ↓ (retourne ChoiceMessage sans tool_calls)
79
- PydanticAI Agent
80
- ↓ (reçoit tool_calls = [])
81
- ```
82
-
83
- ## ✅ Solution : Ajouter le Support des Tool Calls
84
-
85
- ### Étape 1 : Mettre à Jour le Modèle de Requête
86
-
87
- ```python
88
- # app/models/openai.py
89
-
90
- from typing import List, Literal, Optional, Dict, Any
91
- from pydantic import BaseModel, Field
92
-
93
- class Function(BaseModel):
94
- name: str
95
- description: Optional[str] = None
96
- parameters: Dict[str, Any]
97
-
98
- class Tool(BaseModel):
99
- type: Literal["function"] = "function"
100
- function: Function
101
-
102
- class ChatCompletionRequest(BaseModel):
103
- model: Optional[str] = None
104
- messages: List[Message]
105
- temperature: Optional[float] = 0.7
106
- max_tokens: Optional[int] = None
107
- stream: Optional[bool] = False
108
- top_p: Optional[float] = 1.0
109
- tools: Optional[List[Tool]] = None # ✅ AJOUTER
110
- tool_choice: Optional[Union[Literal["none", "auto"], Dict[str, Any]]] = None # ✅ AJOUTER
111
- ```
112
-
113
- ### Étape 2 : Mettre à Jour le Modèle de Réponse
114
-
115
- ```python
116
- # app/models/openai.py
117
-
118
- class FunctionCall(BaseModel):
119
- name: str
120
- arguments: str # JSON string
121
-
122
- class ToolCall(BaseModel):
123
- id: str
124
- type: Literal["function"] = "function"
125
- function: FunctionCall
126
-
127
- class ChoiceMessage(BaseModel):
128
- role: Literal["assistant"]
129
- content: Optional[str] = None
130
- tool_calls: Optional[List[ToolCall]] = None # ✅ AJOUTER
131
- ```
132
-
133
- ### Étape 3 : Mettre à Jour le Provider
134
-
135
- Le provider doit :
136
-
137
- 1. **Extraire les tools du payload**
138
- 2. **Inclure les tools dans le prompt** (format spécial pour Qwen)
139
- 3. **Parser la réponse** pour détecter les tool calls
140
- 4. **Retourner les tool calls** dans la réponse
141
-
142
- **Option A : Format Textuel (Plus Simple)**
143
-
144
- Si le modèle génère des tool calls en texte, parser la réponse :
145
-
146
- ```python
147
- def _parse_tool_calls(self, generated_text: str, tools: List[Tool]) -> List[ToolCall]:
148
- """Parse tool calls from generated text."""
149
- # Chercher des patterns comme:
150
- # <tool_call>
151
- # {"name": "calculer_valeur_future", "arguments": "{\"capital_initial\": 10000}"}
152
- # </tool_call>
153
- import re
154
- import json
155
-
156
- tool_calls = []
157
- pattern = r'<tool_call>\s*({.*?})\s*</tool_call>'
158
- matches = re.findall(pattern, generated_text, re.DOTALL)
159
-
160
- for i, match in enumerate(matches):
161
- try:
162
- call_data = json.loads(match)
163
- tool_calls.append(ToolCall(
164
- id=f"call_{i}",
165
- type="function",
166
- function=FunctionCall(
167
- name=call_data["name"],
168
- arguments=json.dumps(call_data.get("arguments", {}))
169
- )
170
- ))
171
- except Exception as e:
172
- logger.warning(f"Failed to parse tool call: {e}")
173
-
174
- return tool_calls
175
- ```
176
-
177
- **Option B : Format JSON Structured Output**
178
-
179
- Si le modèle supporte le JSON mode, forcer un format structuré :
180
-
181
- ```python
182
- # Dans le prompt, ajouter:
183
- # "You must respond in JSON format with tool_calls array"
184
- # Puis parser le JSON
185
- ```
186
-
187
- ### Étape 4 : Mettre à Jour le Router
188
-
189
- Le router doit passer les tools au provider :
190
-
191
- ```python
192
- # app/routers/openai_api.py
193
-
194
- payload: Dict[str, Any] = {
195
- "model": body.model or settings.model,
196
- "messages": [m.model_dump() for m in body.messages],
197
- "temperature": body.temperature or 0.7,
198
- "top_p": body.top_p or 1.0,
199
- "stream": body.stream or False,
200
- }
201
-
202
- # ✅ AJOUTER
203
- if body.tools:
204
- payload["tools"] = [t.model_dump() for t in body.tools]
205
- if body.tool_choice:
206
- payload["tool_choice"] = body.tool_choice
207
- ```
208
-
209
- ## 🎯 Stratégie de Mise en Œuvre
210
-
211
- ### Phase 1 : Support Basique (Textuel)
212
-
213
- 1. ✅ Ajouter `tools` et `tool_choice` au modèle de requête
214
- 2. ✅ Ajouter `tool_calls` au modèle de réponse
215
- 3. ✅ Parser les tool calls depuis le texte généré
216
- 4. ✅ Retourner les tool calls dans la réponse
217
-
218
- ### Phase 2 : Support Avancé (Structured Output)
219
-
220
- 1. 🔄 Forcer le modèle à générer du JSON structuré
221
- 2. 🔄 Parser le JSON pour extraire les tool calls
222
- 3. 🔄 Valider les tool calls contre les tools fournis
223
-
224
- ### Phase 3 : Support Complet (Native)
225
-
226
- 1. 🎯 Fine-tuner le modèle pour générer des tool calls natifs
227
- 2. 🎯 Utiliser un format de sortie spécialisé
228
- 3. 🎯 Support complet du format OpenAI
229
-
230
- ## 📝 Notes Importantes
231
-
232
- ### Limitations du Modèle Qwen 8B
233
-
234
- Le modèle Qwen 8B fine-tuné peut :
235
- - ✅ Générer du texte qui mentionne les outils
236
- - ❌ Ne pas générer de tool calls au format OpenAI natif
237
- - ❌ Ne pas structurer la réponse avec `tool_calls`
238
-
239
- ### Solutions de Contournement
240
-
241
- 1. **Parser le texte** : Extraire les tool calls depuis le texte généré
242
- 2. **Format spécialisé** : Utiliser un format de prompt spécial pour forcer les tool calls
243
- 3. **Post-processing** : Analyser la réponse et exécuter les outils mentionnés
244
-
245
- ## 🔗 Fichiers à Modifier
246
-
247
- 1. `app/models/openai.py` : Ajouter `tools`, `tool_choice`, `tool_calls`
248
- 2. `app/providers/transformers_provider.py` : Gérer les tools et parser les tool calls
249
- 3. `app/routers/openai_api.py` : Passer les tools au provider
250
- 4. Tests : Ajouter des tests pour les tool calls
251
-
252
- ## 📚 Références
253
-
254
- - [OpenAI Tool Calls Format](https://platform.openai.com/docs/guides/function-calling)
255
- - [PydanticAI Tools Documentation](https://ai.pydantic.dev/tools/)
256
- - [Qwen Model Documentation](https://huggingface.co/Qwen)
257
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/conftest.py CHANGED
@@ -1,7 +1,6 @@
1
  import os
2
  import sys
3
 
4
-
5
  # Ensure project root is on sys.path so `import app` works in tests
6
  ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
7
  if ROOT not in sys.path:
 
1
  import os
2
  import sys
3
 
 
4
  # Ensure project root is on sys.path so `import app` works in tests
5
  ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
6
  if ROOT not in sys.path:
tests/integration/__init__.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ """Integration tests for the OpenAI-compatible API.
2
+
3
+ These tests evaluate the model's ability to:
4
+ - Produce valid JSON outputs
5
+ - Execute tool calls correctly
6
+ - Handle structured data requirements
7
+
8
+ Critical for financial applications where tool execution and structured outputs are mandatory.
9
+ """
10
+
test_space_basic.py → tests/integration/test_space_basic.py RENAMED
File without changes
test_space_with_tools.py → tests/integration/test_space_with_tools.py RENAMED
File without changes
test_tool_calls.py → tests/integration/test_tool_calls.py RENAMED
File without changes
tests/test_openai_models.py CHANGED
@@ -53,7 +53,7 @@ def test_chat_completion_request_defaults():
53
  )
54
 
55
  assert request.model == "test-model"
56
- assert request.temperature == 0.2
57
  assert request.max_tokens is None
58
  assert request.stream is False
59
 
 
53
  )
54
 
55
  assert request.model == "test-model"
56
+ assert request.temperature == 0.7 # Default temperature
57
  assert request.max_tokens is None
58
  assert request.stream is False
59