Skip to content

Commit 37f67ac

Browse files
fix(api): enhance OpenAI API compatibility for frontend integration (Issue #113) (#123)
- Add optional OpenAI-standard fields to Model struct: permission, root, parent - Use skip_serializing_if to omit null fields for cleaner JSON responses - Populate root field with model name for standard OpenAI compatibility - Update regression tests to verify enhanced Model structure - Improves compatibility with frontend tools that expect full OpenAI API format Resolves Issue #113: OpenAI API compatibility for frontends Signed-off-by: Michael A. Kuykendall <michaelallenkuykendall@gmail.com>
1 parent 5c79b2d commit 37f67ac

2 files changed

Lines changed: 24 additions & 2 deletions

File tree

src/openai_compat.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ pub struct Model {
7777
pub object: String,
7878
pub created: u64,
7979
pub owned_by: String,
80+
#[serde(skip_serializing_if = "Option::is_none")]
81+
pub permission: Option<Vec<String>>,
82+
#[serde(skip_serializing_if = "Option::is_none")]
83+
pub root: Option<String>,
84+
#[serde(skip_serializing_if = "Option::is_none")]
85+
pub parent: Option<String>,
8086
}
8187

8288
pub async fn models(State(state): State<Arc<AppState>>) -> impl IntoResponse {
@@ -85,10 +91,13 @@ pub async fn models(State(state): State<Arc<AppState>>) -> impl IntoResponse {
8591
.list_all_available()
8692
.into_iter()
8793
.map(|name| Model {
88-
id: name,
94+
id: name.clone(),
8995
object: "model".to_string(),
9096
created: 0, // Fixed timestamp for simplicity
9197
owned_by: "shimmy".to_string(),
98+
permission: None, // No fine-grained permissions for local models
99+
root: Some(name), // The model itself is the root
100+
parent: None, // Local models don't have parent models
92101
})
93102
.collect();
94103

tests/regression_tests.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,18 @@ mod regression_tests {
203203
object: "model".to_string(),
204204
created: 0,
205205
owned_by: "shimmy".to_string(),
206+
permission: None,
207+
root: Some("qwen3-4b-instruct".to_string()),
208+
parent: None,
206209
},
207210
Model {
208211
id: "llama-7b".to_string(),
209212
object: "model".to_string(),
210213
created: 0,
211214
owned_by: "shimmy".to_string(),
215+
permission: None,
216+
root: Some("llama-7b".to_string()),
217+
parent: None,
212218
},
213219
],
214220
};
@@ -316,12 +322,15 @@ mod regression_tests {
316322
// Test the fix for Issue #113 - OpenAI API compatibility for frontends
317323
use shimmy::openai_compat::{Model, ModelsResponse};
318324

319-
// Test basic Model structure (enhanced fields are on PR branch)
325+
// Test enhanced Model structure with frontend compatibility fields
320326
let model = Model {
321327
id: "test-model".to_string(),
322328
object: "model".to_string(),
323329
created: 1640995200,
324330
owned_by: "shimmy".to_string(),
331+
permission: None,
332+
root: Some("test-model".to_string()),
333+
parent: None,
325334
};
326335

327336
// Test serialization works correctly
@@ -330,6 +339,10 @@ mod regression_tests {
330339
assert_eq!(json["owned_by"], "shimmy");
331340
assert_eq!(json["object"], "model");
332341
assert_eq!(json["created"], 1640995200);
342+
assert_eq!(json["root"], "test-model");
343+
// Optional fields should be omitted when None (due to skip_serializing_if)
344+
assert!(json.get("permission").is_none());
345+
assert!(json.get("parent").is_none());
333346

334347
// Test ModelsResponse structure
335348
let response = ModelsResponse {

0 commit comments

Comments
 (0)