@leoustc/service-llm 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -144,9 +144,10 @@ Connect an MCP client to:
144
144
  http://127.0.0.1:7060/mcp
145
145
  ```
146
146
 
147
- The server provides one tool:
147
+ The server provides two tools:
148
148
 
149
149
  - `ask`: accepts `prompt` and optional `model`, `tools`, and `tool_choice` arguments.
150
+ - `list_models`: lists available models and reports the currently configured default model and reasoning level.
150
151
 
151
152
  Example JSON-RPC request:
152
153
 
@@ -168,6 +169,14 @@ curl http://127.0.0.1:7060/mcp \
168
169
 
169
170
  Specify a model with the request's `model` field. If it is omitted or unavailable, the service uses your configured default. On first login, the lowest-cost available model is selected with low reasoning effort. Responses always report the model actually used.
170
171
 
172
+ List the models available to both Chat Completions and Responses clients:
173
+
174
+ ```sh
175
+ curl http://127.0.0.1:7060/v1/models
176
+ ```
177
+
178
+ When client authentication is enabled, include the same Bearer or `X-API-Key` header used for generation requests. The response includes `default_model` and `default_reasoning_effort` in addition to the OpenAI-compatible model list.
179
+
171
180
  ## Debug logging
172
181
 
173
182
  Enable request, response, and SSE logs with:
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.2.3
package/dist/http.js CHANGED
@@ -26,6 +26,23 @@ function sendJson(response, status, body) {
26
26
  response.end(JSON.stringify(body));
27
27
  }
28
28
 
29
+ export function modelCatalogResponse(catalog) {
30
+ const created = Math.floor(new Date(catalog.updated_at ?? 0).getTime() / 1000) || 0;
31
+ return {
32
+ object: "list",
33
+ data: catalog.models.map((model) => ({
34
+ id: model.id,
35
+ object: "model",
36
+ created,
37
+ owned_by: "openai-codex",
38
+ ...(model.name ? { name: model.name } : {}),
39
+ })),
40
+ default_model: catalog.default_model,
41
+ default_reasoning_effort: catalog.default_reasoning_effort ?? "low",
42
+ updated_at: catalog.updated_at,
43
+ };
44
+ }
45
+
29
46
  async function readJson(request) {
30
47
  const chunks = [];
31
48
  let size = 0;
@@ -167,7 +184,20 @@ async function mcp(response, body) {
167
184
  if (body.method === "initialize") return sendJson(response, 200, { ...base, result: { protocolVersion: body.params?.protocolVersion ?? "2025-03-26", capabilities: { tools: {} }, serverInfo: { name: "service-llm", version } } });
168
185
  if (body.method === "notifications/initialized") return response.writeHead(202).end();
169
186
  if (body.method === "ping") return sendJson(response, 200, { ...base, result: {} });
170
- if (body.method === "tools/list") return sendJson(response, 200, { ...base, result: { tools: [{ name: "ask", description: "Ask a Codex model a question and optionally allow it to return function tool calls. Tool calls are never executed.", inputSchema: { type: "object", properties: { prompt: { type: "string" }, model: { type: "string" }, tools: { type: "array", items: { type: "object" } }, tool_choice: {} }, required: ["prompt"], additionalProperties: false } }] } });
187
+ if (body.method === "tools/list") return sendJson(response, 200, { ...base, result: { tools: [
188
+ { name: "ask", description: "Ask a Codex model a question and optionally allow it to return function tool calls. Tool calls are never executed.", inputSchema: { type: "object", properties: { prompt: { type: "string" }, model: { type: "string" }, tools: { type: "array", items: { type: "object" } }, tool_choice: {} }, required: ["prompt"], additionalProperties: false } },
189
+ { name: "list_models", description: "List the available Codex models and report the model currently configured as the default.", inputSchema: { type: "object", properties: {}, additionalProperties: false } },
190
+ ] } });
191
+ if (body.method === "tools/call" && body.params?.name === "list_models") {
192
+ const catalog = modelCatalogResponse(await listModels());
193
+ return sendJson(response, 200, {
194
+ ...base,
195
+ result: {
196
+ content: [{ type: "text", text: JSON.stringify(catalog, null, 2) }],
197
+ structuredContent: catalog,
198
+ },
199
+ });
200
+ }
171
201
  if (body.method === "tools/call" && body.params?.name === "ask") {
172
202
  if (typeof body.params.arguments?.prompt !== "string") return sendJson(response, 200, { ...base, error: { code: -32602, message: "ask requires a string prompt" } });
173
203
  const answer = await ask({
@@ -209,6 +239,7 @@ export function startServer({ port = 7060, host = "127.0.0.1", apiKeys = [] } =
209
239
  });
210
240
  if (request.method === "GET" && url.pathname === "/health") return sendJson(response, 200, { status: "ok" });
211
241
  if (!isAuthorized(request, allowedKeys)) return sendJson(response, 401, { error: { message: "Invalid API key", type: "authentication_error" } });
242
+ if (request.method === "GET" && url.pathname === "/v1/models") return sendJson(response, 200, modelCatalogResponse(await listModels()));
212
243
  if (request.method !== "POST") return sendJson(response, 405, { error: { message: "Method not allowed" } });
213
244
  const body = await readJson(request);
214
245
  debug("request body", body);
@@ -245,6 +276,7 @@ export function startServer({ port = 7060, host = "127.0.0.1", apiKeys = [] } =
245
276
  }
246
277
  console.error("Endpoints:");
247
278
  console.error(` Health: ${baseUrl}/health`);
279
+ console.error(` Models: ${baseUrl}/v1/models`);
248
280
  console.error(` Chat Completions: ${baseUrl}/v1/chat/completions`);
249
281
  console.error(` Responses: ${baseUrl}/v1/responses`);
250
282
  console.error(` MCP: ${baseUrl}/mcp`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leoustc/service-llm",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Standalone CLI service exposing a Codex subscription through OpenAI-compatible and MCP endpoints.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -28,5 +28,13 @@
28
28
  "publishConfig": {
29
29
  "access": "public"
30
30
  },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/leoustc/service-llm.git"
34
+ },
35
+ "homepage": "https://github.com/leoustc/service-llm#readme",
36
+ "bugs": {
37
+ "url": "https://github.com/leoustc/service-llm/issues"
38
+ },
31
39
  "license": "Apache-2.0"
32
40
  }