@llmindset/hf-mcp 0.2.39 → 0.2.40
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/dist/docs-search/docs-semantic-search.test.js +20 -44
- package/dist/docs-search/docs-semantic-search.test.js.map +1 -1
- package/dist/hf-api-call.d.ts.map +1 -1
- package/dist/hf-api-call.js +21 -7
- package/dist/hf-api-call.js.map +1 -1
- package/dist/jobs/commands/inspect.js +1 -1
- package/dist/jobs/commands/inspect.js.map +1 -1
- package/dist/jobs/commands/logs.js +3 -3
- package/dist/jobs/commands/logs.js.map +1 -1
- package/dist/jobs/commands/ps.js +1 -1
- package/dist/jobs/commands/ps.js.map +1 -1
- package/dist/jobs/commands/run.js +6 -6
- package/dist/jobs/commands/run.js.map +1 -1
- package/dist/jobs/commands/scheduled.js +5 -5
- package/dist/jobs/commands/scheduled.js.map +1 -1
- package/dist/jobs/jobs-tool.d.ts +4 -4
- package/dist/jobs/jobs-tool.d.ts.map +1 -1
- package/dist/jobs/jobs-tool.js +202 -137
- package/dist/jobs/jobs-tool.js.map +1 -1
- package/dist/jobs/schema-help.d.ts +1 -2
- package/dist/jobs/schema-help.d.ts.map +1 -1
- package/dist/jobs/sse-handler.d.ts +3 -0
- package/dist/jobs/sse-handler.d.ts.map +1 -1
- package/dist/jobs/sse-handler.js +4 -1
- package/dist/jobs/sse-handler.js.map +1 -1
- package/dist/jobs/types.d.ts +4 -4
- package/dist/jobs/types.d.ts.map +1 -1
- package/dist/jobs/types.js +8 -4
- package/dist/jobs/types.js.map +1 -1
- package/package.json +1 -1
- package/src/docs-search/docs-semantic-search.test.ts +22 -44
- package/src/hf-api-call.ts +30 -8
- package/src/jobs/commands/inspect.ts +1 -1
- package/src/jobs/commands/logs.ts +3 -3
- package/src/jobs/commands/ps.ts +1 -1
- package/src/jobs/commands/run.ts +6 -6
- package/src/jobs/commands/scheduled.ts +5 -5
- package/src/jobs/jobs-tool.ts +231 -149
- package/src/jobs/schema-help.ts +1 -1
- package/src/jobs/sse-handler.ts +16 -1
- package/src/jobs/types.ts +8 -4
package/src/jobs/jobs-tool.ts
CHANGED
|
@@ -14,9 +14,10 @@ import {
|
|
|
14
14
|
scheduledSuspendCommand,
|
|
15
15
|
scheduledResumeCommand,
|
|
16
16
|
} from './commands/scheduled.js';
|
|
17
|
-
import { formatCommandHelp } from './schema-help.js';
|
|
17
|
+
import { formatCommandHelp, extractFieldDetails, type AnyZodType } from './schema-help.js';
|
|
18
18
|
import type { ToolResult } from '../types/tool-result.js';
|
|
19
19
|
import { CPU_FLAVORS, GPU_FLAVORS, SPECIALIZED_FLAVORS } from './types.js';
|
|
20
|
+
import { DEFAULT_LOG_WAIT_SECONDS } from './sse-handler.js';
|
|
20
21
|
import type {
|
|
21
22
|
RunArgs,
|
|
22
23
|
UvArgs,
|
|
@@ -48,10 +49,90 @@ import {
|
|
|
48
49
|
scheduledJobArgsSchema,
|
|
49
50
|
} from './types.js';
|
|
50
51
|
|
|
52
|
+
const OPERATION_NAMES = [
|
|
53
|
+
'run',
|
|
54
|
+
'uv',
|
|
55
|
+
'ps',
|
|
56
|
+
'logs',
|
|
57
|
+
'inspect',
|
|
58
|
+
'cancel',
|
|
59
|
+
'scheduled run',
|
|
60
|
+
'scheduled uv',
|
|
61
|
+
'scheduled ps',
|
|
62
|
+
'scheduled inspect',
|
|
63
|
+
'scheduled delete',
|
|
64
|
+
'scheduled suspend',
|
|
65
|
+
'scheduled resume',
|
|
66
|
+
] as const;
|
|
67
|
+
|
|
68
|
+
type OperationName = (typeof OPERATION_NAMES)[number];
|
|
69
|
+
|
|
70
|
+
const OPERATION_EXAMPLES: Partial<Record<OperationName, string>> = {
|
|
71
|
+
run: `{
|
|
72
|
+
"operation": "run",
|
|
73
|
+
"args": {
|
|
74
|
+
"image": "python:3.12",
|
|
75
|
+
"command": ["python", "-c", "print('Hello from HF Jobs!')"],
|
|
76
|
+
"flavor": "cpu-basic"
|
|
77
|
+
}
|
|
78
|
+
}`,
|
|
79
|
+
uv: `{
|
|
80
|
+
"operation": "uv",
|
|
81
|
+
"args": {
|
|
82
|
+
"script": "import random\\nprint(42 + random.randint(1, 5))"
|
|
83
|
+
}
|
|
84
|
+
}`,
|
|
85
|
+
ps: `{"operation": "ps"}`,
|
|
86
|
+
logs: `{
|
|
87
|
+
"operation": "logs",
|
|
88
|
+
"args": {"job_id": "your-job-id"}
|
|
89
|
+
}`,
|
|
90
|
+
inspect: `{
|
|
91
|
+
"operation": "inspect",
|
|
92
|
+
"args": {"job_id": "your-job-id"}
|
|
93
|
+
}`,
|
|
94
|
+
cancel: `{
|
|
95
|
+
"operation": "cancel",
|
|
96
|
+
"args": {"job_id": "your-job-id"}
|
|
97
|
+
}`,
|
|
98
|
+
'scheduled run': `{
|
|
99
|
+
"operation": "scheduled run",
|
|
100
|
+
"args": {
|
|
101
|
+
"schedule": "@hourly",
|
|
102
|
+
"image": "python:3.12",
|
|
103
|
+
"command": ["python", "backup.py"]
|
|
104
|
+
}
|
|
105
|
+
}`,
|
|
106
|
+
'scheduled uv': `{
|
|
107
|
+
"operation": "scheduled uv",
|
|
108
|
+
"args": {
|
|
109
|
+
"schedule": "0 9 * * 1-5",
|
|
110
|
+
"script": "import datetime\\nprint('daily check', datetime.datetime.utcnow())"
|
|
111
|
+
}
|
|
112
|
+
}`,
|
|
113
|
+
'scheduled ps': `{"operation": "scheduled ps"}`,
|
|
114
|
+
'scheduled inspect': `{
|
|
115
|
+
"operation": "scheduled inspect",
|
|
116
|
+
"args": {"scheduled_job_id": "your-scheduled-job-id"}
|
|
117
|
+
}`,
|
|
118
|
+
'scheduled delete': `{
|
|
119
|
+
"operation": "scheduled delete",
|
|
120
|
+
"args": {"scheduled_job_id": "your-scheduled-job-id"}
|
|
121
|
+
}`,
|
|
122
|
+
'scheduled suspend': `{
|
|
123
|
+
"operation": "scheduled suspend",
|
|
124
|
+
"args": {"scheduled_job_id": "your-scheduled-job-id"}
|
|
125
|
+
}`,
|
|
126
|
+
'scheduled resume': `{
|
|
127
|
+
"operation": "scheduled resume",
|
|
128
|
+
"args": {"scheduled_job_id": "your-scheduled-job-id"}
|
|
129
|
+
}`,
|
|
130
|
+
};
|
|
131
|
+
|
|
51
132
|
/**
|
|
52
|
-
* Map of
|
|
133
|
+
* Map of operation names to their validation schemas
|
|
53
134
|
*/
|
|
54
|
-
const
|
|
135
|
+
const OPERATION_SCHEMAS: Record<OperationName, z.ZodSchema> = {
|
|
55
136
|
run: runArgsSchema,
|
|
56
137
|
uv: uvArgsSchema,
|
|
57
138
|
ps: psArgsSchema,
|
|
@@ -65,9 +146,10 @@ const COMMAND_SCHEMAS = {
|
|
|
65
146
|
'scheduled delete': scheduledJobArgsSchema,
|
|
66
147
|
'scheduled suspend': scheduledJobArgsSchema,
|
|
67
148
|
'scheduled resume': scheduledJobArgsSchema,
|
|
68
|
-
}
|
|
149
|
+
};
|
|
69
150
|
|
|
70
151
|
const HELP_FLAG = 'help';
|
|
152
|
+
const operationRequiresArgsCache = new Map<OperationName, boolean>();
|
|
71
153
|
|
|
72
154
|
const CPU_FLAVOR_LIST = CPU_FLAVORS.join(', ');
|
|
73
155
|
const GPU_FLAVOR_LIST = GPU_FLAVORS.join(', ');
|
|
@@ -98,19 +180,83 @@ function removeHelpFlag(args: Record<string, unknown> | undefined): Record<strin
|
|
|
98
180
|
return rest;
|
|
99
181
|
}
|
|
100
182
|
|
|
183
|
+
function isOperationName(value: string): value is OperationName {
|
|
184
|
+
return (OPERATION_NAMES as readonly string[]).includes(value);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function formatExampleSnippet(operation: OperationName): string | undefined {
|
|
188
|
+
const example = OPERATION_EXAMPLES[operation];
|
|
189
|
+
if (!example) {
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return `Call this tool with:\n\`\`\`json\n${example}\n\`\`\``;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function renderExampleSection(title: string, operation: OperationName): string {
|
|
197
|
+
const snippet = formatExampleSnippet(operation);
|
|
198
|
+
if (!snippet) {
|
|
199
|
+
return '';
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return `### ${title}
|
|
203
|
+
${snippet}
|
|
204
|
+
`;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function operationRequiresArgs(operation: OperationName): boolean {
|
|
208
|
+
const cached = operationRequiresArgsCache.get(operation);
|
|
209
|
+
if (cached !== undefined) {
|
|
210
|
+
return cached;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const schema = OPERATION_SCHEMAS[operation];
|
|
214
|
+
if (!schema) {
|
|
215
|
+
operationRequiresArgsCache.set(operation, false);
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const fields = extractFieldDetails(schema as AnyZodType);
|
|
220
|
+
const requiresArgs = fields.some((field) => !field.isOptional);
|
|
221
|
+
operationRequiresArgsCache.set(operation, requiresArgs);
|
|
222
|
+
return requiresArgs;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function formatCommandHelpWithExample(operation: OperationName, schema: z.ZodSchema): string {
|
|
226
|
+
let help = formatCommandHelp(operation, schema);
|
|
227
|
+
const exampleSnippet = formatExampleSnippet(operation);
|
|
228
|
+
|
|
229
|
+
if (exampleSnippet) {
|
|
230
|
+
help += `\n\n### Example\n${exampleSnippet}`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return help;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function extractTopLevelArgs(params: { [key: string]: unknown }): Record<string, unknown> {
|
|
237
|
+
const legacyArgs: Record<string, unknown> = {};
|
|
238
|
+
for (const [key, value] of Object.entries(params)) {
|
|
239
|
+
if (key === 'operation' || key === 'args') {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
legacyArgs[key] = value;
|
|
243
|
+
}
|
|
244
|
+
return legacyArgs;
|
|
245
|
+
}
|
|
246
|
+
|
|
101
247
|
/**
|
|
102
|
-
* Validate
|
|
248
|
+
* Validate operation arguments against a Zod schema
|
|
103
249
|
* Returns a ToolResult with detailed error message if validation fails
|
|
104
250
|
*/
|
|
105
|
-
function validateArgs(
|
|
106
|
-
schema:
|
|
251
|
+
function validateArgs<T extends z.ZodTypeAny>(
|
|
252
|
+
schema: T,
|
|
107
253
|
args: unknown,
|
|
108
|
-
|
|
109
|
-
): { success: true } | { success: false; errorResult: ToolResult } {
|
|
254
|
+
operationName: string
|
|
255
|
+
): { success: true; data: z.infer<T> } | { success: false; errorResult: ToolResult } {
|
|
110
256
|
const result = schema.safeParse(args);
|
|
111
257
|
|
|
112
258
|
if (result.success) {
|
|
113
|
-
return { success: true };
|
|
259
|
+
return { success: true, data: result.data as z.infer<T> };
|
|
114
260
|
}
|
|
115
261
|
|
|
116
262
|
// Format Zod errors into a helpful message
|
|
@@ -127,7 +273,7 @@ function validateArgs(
|
|
|
127
273
|
}
|
|
128
274
|
}
|
|
129
275
|
|
|
130
|
-
let errorMessage = `Error: Invalid parameters for '${
|
|
276
|
+
let errorMessage = `Error: Invalid parameters for '${operationName}'\n\n`;
|
|
131
277
|
|
|
132
278
|
if (missingFields.length > 0) {
|
|
133
279
|
errorMessage += `Missing required parameters:\n${missingFields.join('\n')}\n\n`;
|
|
@@ -137,7 +283,7 @@ function validateArgs(
|
|
|
137
283
|
errorMessage += `Invalid parameters:\n${invalidFields.join('\n')}\n\n`;
|
|
138
284
|
}
|
|
139
285
|
|
|
140
|
-
errorMessage += `Call
|
|
286
|
+
errorMessage += `Call this tool with {"operation": "${operationName}", "args": {"help": true}} to see valid arguments.`;
|
|
141
287
|
|
|
142
288
|
return {
|
|
143
289
|
success: false,
|
|
@@ -178,97 +324,7 @@ Manage compute jobs on Hugging Face infrastructure.
|
|
|
178
324
|
|
|
179
325
|
## Examples
|
|
180
326
|
|
|
181
|
-
|
|
182
|
-
\`\`\`
|
|
183
|
-
hf_jobs("run", {
|
|
184
|
-
"image": "python:3.12",
|
|
185
|
-
"command": ["python", "-c", "print('Hello from HF Jobs!')"],
|
|
186
|
-
"flavor": "cpu-basic"
|
|
187
|
-
})
|
|
188
|
-
\`\`\`
|
|
189
|
-
|
|
190
|
-
### Use a Hugging Face Space as the image
|
|
191
|
-
\`\`\`
|
|
192
|
-
hf_jobs("run", {
|
|
193
|
-
"image": "hf.co/spaces/username/spacename",
|
|
194
|
-
"command": ["python", "app.py"],
|
|
195
|
-
"flavor": "cpu-basic"
|
|
196
|
-
})
|
|
197
|
-
\`\`\`
|
|
198
|
-
|
|
199
|
-
### Run multiline Python scripts
|
|
200
|
-
\`\`\`
|
|
201
|
-
hf_jobs("run", {
|
|
202
|
-
"image": "python:3.12",
|
|
203
|
-
"command": ["python", "-c", "import sys\\nprint('Line 1')\\nprint('Line 2')"],
|
|
204
|
-
"flavor": "cpu-basic"
|
|
205
|
-
})
|
|
206
|
-
\`\`\`
|
|
207
|
-
|
|
208
|
-
### Run a Python Script from a URL with UV
|
|
209
|
-
\`\`\`
|
|
210
|
-
hf_jobs("uv", {
|
|
211
|
-
"script": "https://huggingface.co/datasets/uv-scripts/classification/blob/main/classify-dataset.py",
|
|
212
|
-
"with_deps": ["pandas"],
|
|
213
|
-
"script_args": ["--input", "data.csv"],
|
|
214
|
-
"flavor": "cpu-basic"
|
|
215
|
-
})
|
|
216
|
-
\`\`\`
|
|
217
|
-
|
|
218
|
-
### Run an inline Python script with UV
|
|
219
|
-
\`\`\`
|
|
220
|
-
hf_jobs("uv", {
|
|
221
|
-
"script": "import math\\nprint('area:', math.pi * 4**2)"
|
|
222
|
-
})
|
|
223
|
-
\`\`\`
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
### Run bash/shell commands
|
|
227
|
-
\`\`\`
|
|
228
|
-
hf_jobs("run", {
|
|
229
|
-
"image": "ubuntu:22.04",
|
|
230
|
-
"command": ["/bin/sh", "-lc", "apt-get update && apt-get install -y curl"],
|
|
231
|
-
"flavor": "cpu-basic"
|
|
232
|
-
})
|
|
233
|
-
\`\`\`
|
|
234
|
-
|
|
235
|
-
### List running jobs
|
|
236
|
-
\`\`\`
|
|
237
|
-
hf_jobs("ps")
|
|
238
|
-
\`\`\`
|
|
239
|
-
|
|
240
|
-
### Get job logs
|
|
241
|
-
\`\`\`
|
|
242
|
-
hf_jobs("logs", {"job_id": "your-job-id"})
|
|
243
|
-
\`\`\`
|
|
244
|
-
|
|
245
|
-
### Run with GPU
|
|
246
|
-
\`\`\`
|
|
247
|
-
hf_jobs("run", {
|
|
248
|
-
"image": "pytorch/pytorch:2.6.0-cuda12.4-cudnn9-devel",
|
|
249
|
-
"command": ["python", "train.py"],
|
|
250
|
-
"flavor": "a10g-small"
|
|
251
|
-
})
|
|
252
|
-
\`\`\`
|
|
253
|
-
|
|
254
|
-
### Schedule a job
|
|
255
|
-
\`\`\`
|
|
256
|
-
hf_jobs("scheduled run", {
|
|
257
|
-
"schedule": "@hourly",
|
|
258
|
-
"image": "python:3.12",
|
|
259
|
-
"command": ["python", "backup.py"]
|
|
260
|
-
})
|
|
261
|
-
\`\`\`
|
|
262
|
-
|
|
263
|
-
### Schedule a UV script
|
|
264
|
-
\`\`\`
|
|
265
|
-
hf_jobs("scheduled uv", {
|
|
266
|
-
"schedule": "0 9 * * 1-5",
|
|
267
|
-
"script": "https://huggingface.co/datasets/uv-scripts/classification/blob/main/classify-dataset.py",
|
|
268
|
-
"with_deps": ["pandas"],
|
|
269
|
-
"script_args": ["--input", "data.csv"]
|
|
270
|
-
})
|
|
271
|
-
\`\`\`
|
|
327
|
+
${renderExampleSection('Run a simple job', 'run')}${renderExampleSection('Run a Python script with UV', 'uv')}
|
|
272
328
|
|
|
273
329
|
## Hardware Flavors
|
|
274
330
|
|
|
@@ -291,17 +347,18 @@ ${HARDWARE_FLAVORS_SECTION}
|
|
|
291
347
|
- UV inline scripts are automatically base64-decoded inside the container; just send the raw script text
|
|
292
348
|
|
|
293
349
|
### Show command-specific help
|
|
294
|
-
|
|
295
|
-
|
|
350
|
+
Call this tool with:
|
|
351
|
+
\`\`\`json
|
|
352
|
+
{"operation": "<operation>", "args": {"help": true}}
|
|
296
353
|
\`\`\`
|
|
297
354
|
|
|
298
355
|
## Tips
|
|
299
356
|
|
|
300
357
|
- The uv-scripts organisation contains examples for common tasks. dataset_search {'author':'uv-scripts'}
|
|
301
|
-
- Jobs default to detached mode
|
|
358
|
+
- Jobs default to non-detached mode (tail logs for up to ${DEFAULT_LOG_WAIT_SECONDS}s or until completion). Set \`detach: true\` to return immediately.
|
|
302
359
|
- Prefer array commands to avoid shell parsing surprises
|
|
303
360
|
- To access private Hub assets, include \`secrets: { "HF_TOKEN": "$HF_TOKEN" }\` (or \`${'${HF_TOKEN}'}\`) to inject your auth token.
|
|
304
|
-
-
|
|
361
|
+
- When not detached, logs are time-limited (${DEFAULT_LOG_WAIT_SECONDS}s max or until job completes) - check job page for full logs
|
|
305
362
|
`;
|
|
306
363
|
|
|
307
364
|
/**
|
|
@@ -310,20 +367,15 @@ hf_jobs("<command>", {"help": true})
|
|
|
310
367
|
export const HF_JOBS_TOOL_CONFIG = {
|
|
311
368
|
name: 'hf_jobs',
|
|
312
369
|
description:
|
|
313
|
-
'Manage
|
|
314
|
-
'execute Python scripts with UV, schedule and monitor jobs
|
|
315
|
-
'Call
|
|
316
|
-
'Supports CPU and GPU hardware.',
|
|
370
|
+
'Manage Hugging Face CPU/GPU compute jobs. Run commands in Docker containers, ' +
|
|
371
|
+
'execute Python scripts with UV. List, schedule and monitor jobs/logs. ' +
|
|
372
|
+
'Call this tool with no operation for full usage instructions and examples. ',
|
|
317
373
|
schema: z.object({
|
|
318
|
-
|
|
319
|
-
.
|
|
374
|
+
operation: z
|
|
375
|
+
.enum(OPERATION_NAMES)
|
|
320
376
|
.optional()
|
|
321
|
-
.describe(
|
|
322
|
-
|
|
323
|
-
'"scheduled run", "scheduled uv", "scheduled ps", "scheduled inspect", ' +
|
|
324
|
-
'"scheduled delete", "scheduled suspend", "scheduled resume"'
|
|
325
|
-
),
|
|
326
|
-
args: z.record(z.any()).optional().describe('Command-specific arguments as a JSON object'),
|
|
377
|
+
.describe(`Operation to execute. Valid values: ${OPERATION_NAMES.map((cmd) => `"${cmd}"`).join(', ')}`),
|
|
378
|
+
args: z.record(z.any()).optional().describe('Operation-specific arguments as a JSON object'),
|
|
327
379
|
}),
|
|
328
380
|
annotations: {
|
|
329
381
|
title: 'Hugging Face Jobs', // omit destructive hint.
|
|
@@ -347,9 +399,9 @@ export class HfJobsTool {
|
|
|
347
399
|
}
|
|
348
400
|
|
|
349
401
|
/**
|
|
350
|
-
* Execute a jobs
|
|
402
|
+
* Execute a jobs operation
|
|
351
403
|
*/
|
|
352
|
-
async execute(params: {
|
|
404
|
+
async execute(params: { operation?: string; args?: Record<string, unknown> }): Promise<ToolResult> {
|
|
353
405
|
// If not authenticated, show upgrade message
|
|
354
406
|
if (!this.isAuthenticated) {
|
|
355
407
|
return {
|
|
@@ -360,8 +412,10 @@ export class HfJobsTool {
|
|
|
360
412
|
};
|
|
361
413
|
}
|
|
362
414
|
|
|
363
|
-
|
|
364
|
-
|
|
415
|
+
const requestedOperation = params.operation;
|
|
416
|
+
|
|
417
|
+
// If no operation provided, return usage instructions
|
|
418
|
+
if (!requestedOperation) {
|
|
365
419
|
return {
|
|
366
420
|
formatted: USAGE_INSTRUCTIONS,
|
|
367
421
|
totalResults: 1,
|
|
@@ -369,101 +423,129 @@ export class HfJobsTool {
|
|
|
369
423
|
};
|
|
370
424
|
}
|
|
371
425
|
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
426
|
+
const normalizedOperation = requestedOperation.toLowerCase();
|
|
427
|
+
if (!isOperationName(normalizedOperation)) {
|
|
428
|
+
return {
|
|
429
|
+
formatted: `Unknown operation: "${requestedOperation}"
|
|
430
|
+
Available operations:
|
|
431
|
+
- run, uv, ps, logs, inspect, cancel
|
|
432
|
+
- scheduled run, scheduled uv, scheduled ps, scheduled inspect, scheduled delete, scheduled suspend, scheduled resume
|
|
433
|
+
|
|
434
|
+
Call this tool with no operation for full usage instructions.`,
|
|
435
|
+
totalResults: 0,
|
|
436
|
+
resultsShared: 0,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const operation = normalizedOperation;
|
|
441
|
+
const legacyArgs = extractTopLevelArgs(params as Record<string, unknown>);
|
|
442
|
+
const rawArgs = params.args ? params.args : Object.keys(legacyArgs).length > 0 ? legacyArgs : {};
|
|
443
|
+
const schema = OPERATION_SCHEMAS[operation];
|
|
444
|
+
const noArgsProvided = !params.args || Object.keys(params.args).length === 0;
|
|
445
|
+
|
|
446
|
+
if (schema && noArgsProvided && operationRequiresArgs(operation)) {
|
|
447
|
+
const helpText = formatCommandHelpWithExample(operation, schema);
|
|
448
|
+
return {
|
|
449
|
+
formatted: `No arguments provided for "${operation}".\n\n${helpText}`,
|
|
450
|
+
totalResults: 1,
|
|
451
|
+
resultsShared: 1,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
375
454
|
const helpRequested = isHelpRequested(rawArgs);
|
|
376
455
|
|
|
377
456
|
if (helpRequested) {
|
|
378
457
|
if (!schema) {
|
|
379
458
|
return {
|
|
380
|
-
formatted: `No help available for '${
|
|
459
|
+
formatted: `No help available for '${requestedOperation}'.`,
|
|
381
460
|
totalResults: 0,
|
|
382
461
|
resultsShared: 0,
|
|
383
462
|
};
|
|
384
463
|
}
|
|
385
464
|
|
|
465
|
+
const helpText = formatCommandHelpWithExample(operation, schema);
|
|
386
466
|
return {
|
|
387
|
-
formatted:
|
|
467
|
+
formatted: helpText,
|
|
388
468
|
totalResults: 1,
|
|
389
469
|
resultsShared: 1,
|
|
390
470
|
};
|
|
391
471
|
}
|
|
392
472
|
|
|
393
|
-
const
|
|
473
|
+
const cleanedArgs = removeHelpFlag(rawArgs);
|
|
474
|
+
let parsedArgs: Record<string, unknown> = cleanedArgs;
|
|
394
475
|
|
|
395
|
-
// Validate
|
|
476
|
+
// Validate operation arguments if schema exists
|
|
396
477
|
if (schema) {
|
|
397
|
-
const validation = validateArgs(schema,
|
|
478
|
+
const validation = validateArgs(schema, cleanedArgs, operation);
|
|
398
479
|
if (!validation.success) {
|
|
399
480
|
return validation.errorResult;
|
|
400
481
|
}
|
|
482
|
+
parsedArgs = validation.data as Record<string, unknown>;
|
|
401
483
|
}
|
|
402
484
|
|
|
403
485
|
try {
|
|
404
486
|
let result: string;
|
|
405
487
|
|
|
406
|
-
switch (
|
|
488
|
+
switch (operation) {
|
|
407
489
|
case 'run':
|
|
408
|
-
result = await runCommand(
|
|
490
|
+
result = await runCommand(parsedArgs as RunArgs, this.client, this.hfToken);
|
|
409
491
|
break;
|
|
410
492
|
|
|
411
493
|
case 'uv':
|
|
412
|
-
result = await uvCommand(
|
|
494
|
+
result = await uvCommand(parsedArgs as UvArgs, this.client, this.hfToken);
|
|
413
495
|
break;
|
|
414
496
|
|
|
415
497
|
case 'ps':
|
|
416
|
-
result = await psCommand(
|
|
498
|
+
result = await psCommand(parsedArgs as PsArgs, this.client);
|
|
417
499
|
break;
|
|
418
500
|
|
|
419
501
|
case 'logs':
|
|
420
|
-
result = await logsCommand(
|
|
502
|
+
result = await logsCommand(parsedArgs as LogsArgs, this.client, this.hfToken);
|
|
421
503
|
break;
|
|
422
504
|
|
|
423
505
|
case 'inspect':
|
|
424
|
-
result = await inspectCommand(
|
|
506
|
+
result = await inspectCommand(parsedArgs as InspectArgs, this.client);
|
|
425
507
|
break;
|
|
426
508
|
|
|
427
509
|
case 'cancel':
|
|
428
|
-
result = await cancelCommand(
|
|
510
|
+
result = await cancelCommand(parsedArgs as CancelArgs, this.client);
|
|
429
511
|
break;
|
|
430
512
|
|
|
431
513
|
case 'scheduled run':
|
|
432
|
-
result = await scheduledRunCommand(
|
|
514
|
+
result = await scheduledRunCommand(parsedArgs as ScheduledRunArgs, this.client, this.hfToken);
|
|
433
515
|
break;
|
|
434
516
|
|
|
435
517
|
case 'scheduled uv':
|
|
436
|
-
result = await scheduledUvCommand(
|
|
518
|
+
result = await scheduledUvCommand(parsedArgs as ScheduledUvArgs, this.client, this.hfToken);
|
|
437
519
|
break;
|
|
438
520
|
|
|
439
521
|
case 'scheduled ps':
|
|
440
|
-
result = await scheduledPsCommand(
|
|
522
|
+
result = await scheduledPsCommand(parsedArgs as ScheduledPsArgs, this.client);
|
|
441
523
|
break;
|
|
442
524
|
|
|
443
525
|
case 'scheduled inspect':
|
|
444
|
-
result = await scheduledInspectCommand(
|
|
526
|
+
result = await scheduledInspectCommand(parsedArgs as ScheduledJobArgs, this.client);
|
|
445
527
|
break;
|
|
446
528
|
|
|
447
529
|
case 'scheduled delete':
|
|
448
|
-
result = await scheduledDeleteCommand(
|
|
530
|
+
result = await scheduledDeleteCommand(parsedArgs as ScheduledJobArgs, this.client);
|
|
449
531
|
break;
|
|
450
532
|
|
|
451
533
|
case 'scheduled suspend':
|
|
452
|
-
result = await scheduledSuspendCommand(
|
|
534
|
+
result = await scheduledSuspendCommand(parsedArgs as ScheduledJobArgs, this.client);
|
|
453
535
|
break;
|
|
454
536
|
|
|
455
537
|
case 'scheduled resume':
|
|
456
|
-
result = await scheduledResumeCommand(
|
|
538
|
+
result = await scheduledResumeCommand(parsedArgs as ScheduledJobArgs, this.client);
|
|
457
539
|
break;
|
|
458
540
|
|
|
459
541
|
default:
|
|
460
542
|
return {
|
|
461
|
-
formatted: `Unknown
|
|
462
|
-
Available
|
|
543
|
+
formatted: `Unknown operation: "${requestedOperation ?? 'unknown'}"
|
|
544
|
+
Available operations:
|
|
463
545
|
- run, uv, ps, logs, inspect, cancel
|
|
464
546
|
- scheduled run, scheduled uv, scheduled ps, scheduled inspect, scheduled delete, scheduled suspend, scheduled resume
|
|
465
547
|
|
|
466
|
-
Call
|
|
548
|
+
Call this tool with no operation for full usage instructions.`,
|
|
467
549
|
totalResults: 0,
|
|
468
550
|
resultsShared: 0,
|
|
469
551
|
};
|
|
@@ -493,7 +575,7 @@ Call hf_jobs() with no arguments for full usage instructions.`,
|
|
|
493
575
|
}
|
|
494
576
|
|
|
495
577
|
return {
|
|
496
|
-
formatted: `Error executing ${
|
|
578
|
+
formatted: `Error executing ${requestedOperation ?? 'operation'}: ${errorMessage}`,
|
|
497
579
|
totalResults: 0,
|
|
498
580
|
resultsShared: 0,
|
|
499
581
|
isError: true,
|
package/src/jobs/schema-help.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { z } from 'zod';
|
|
|
5
5
|
* Provides introspection and documentation generation for command arguments
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
type AnyZodType = z.ZodType<unknown, z.ZodTypeDef, unknown>;
|
|
8
|
+
export type AnyZodType = z.ZodType<unknown, z.ZodTypeDef, unknown>;
|
|
9
9
|
|
|
10
10
|
export interface FieldDetails {
|
|
11
11
|
key: string;
|
package/src/jobs/sse-handler.ts
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import type { LogEvent } from './types.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Default duration to wait for logs when not detached (in milliseconds)
|
|
5
|
+
*/
|
|
6
|
+
export const DEFAULT_LOG_WAIT_MS = 10000; // 10 seconds
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Default duration to wait for logs when not detached (in seconds, for display)
|
|
10
|
+
*/
|
|
11
|
+
export const DEFAULT_LOG_WAIT_SECONDS = DEFAULT_LOG_WAIT_MS / 1000;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Default maximum number of log lines to return
|
|
15
|
+
*/
|
|
16
|
+
export const DEFAULT_MAX_LOG_LINES = 20;
|
|
17
|
+
|
|
3
18
|
/**
|
|
4
19
|
* Options for fetching logs via SSE
|
|
5
20
|
*/
|
|
@@ -33,7 +48,7 @@ export interface SseLogResult {
|
|
|
33
48
|
* @returns Log result with collected lines and status
|
|
34
49
|
*/
|
|
35
50
|
export async function fetchJobLogs(url: string, options: SseLogOptions = {}): Promise<SseLogResult> {
|
|
36
|
-
const { maxDuration =
|
|
51
|
+
const { maxDuration = DEFAULT_LOG_WAIT_MS, maxLines = DEFAULT_MAX_LOG_LINES, token } = options;
|
|
37
52
|
|
|
38
53
|
const logLines: string[] = [];
|
|
39
54
|
let finished = false;
|
package/src/jobs/types.ts
CHANGED
|
@@ -133,7 +133,11 @@ const commonArgsSchema = z.object({
|
|
|
133
133
|
|
|
134
134
|
// Run command args
|
|
135
135
|
export const runArgsSchema = commonArgsSchema.extend({
|
|
136
|
-
image: z
|
|
136
|
+
image: z
|
|
137
|
+
.string()
|
|
138
|
+
.describe('Docker image or HF Space URL (e.g., "python:3.12" or "hf.co/spaces/user/space")')
|
|
139
|
+
.optional()
|
|
140
|
+
.default('python:3.12'), // NOTE -- this is a deviation from the hf jobs command (which has no default)
|
|
137
141
|
command: z
|
|
138
142
|
.union([z.string(), z.array(z.string())])
|
|
139
143
|
.describe(
|
|
@@ -155,8 +159,8 @@ export const runArgsSchema = commonArgsSchema.extend({
|
|
|
155
159
|
detach: z
|
|
156
160
|
.boolean()
|
|
157
161
|
.optional()
|
|
158
|
-
.default(
|
|
159
|
-
.describe('
|
|
162
|
+
.default(false)
|
|
163
|
+
.describe('If true, return immediately with job ID. If false (default), tail logs for up to 10 seconds.'),
|
|
160
164
|
});
|
|
161
165
|
|
|
162
166
|
// UV command args
|
|
@@ -175,7 +179,7 @@ export const uvArgsSchema = commonArgsSchema.extend({
|
|
|
175
179
|
.optional()
|
|
176
180
|
.describe('Secrets as key-value pairs. Use HF_TOKEN=$HF_TOKEN to include your token'),
|
|
177
181
|
timeout: z.string().optional().default('30m').describe('Max duration'),
|
|
178
|
-
detach: z.boolean().optional().default(
|
|
182
|
+
detach: z.boolean().optional().default(false).describe('If true, return immediately with job ID. If false (default), tail logs for up to 10 seconds.'),
|
|
179
183
|
});
|
|
180
184
|
|
|
181
185
|
// PS command args
|