@hasna/connectors 1.3.0 → 1.3.1
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/bin/index.js +1 -1
- package/bin/mcp.js +1 -1
- package/connectors/connect-huggingface/src/api/client.ts +5 -0
- package/connectors/connect-huggingface/src/api/datasets.ts +73 -0
- package/connectors/connect-huggingface/src/api/index.ts +17 -19
- package/connectors/connect-huggingface/src/api/inference.ts +100 -0
- package/connectors/connect-huggingface/src/api/models.ts +66 -0
- package/connectors/connect-huggingface/src/api/spaces.ts +42 -0
- package/connectors/connect-huggingface/src/cli/index.ts +148 -36
- package/connectors/connect-openai/src/api/images.ts +52 -8
- package/package.json +1 -1
- package/connectors/connect-huggingface/src/api/example.ts +0 -48
package/bin/index.js
CHANGED
|
@@ -11851,7 +11851,7 @@ import chalk2 from "chalk";
|
|
|
11851
11851
|
// package.json
|
|
11852
11852
|
var package_default = {
|
|
11853
11853
|
name: "@hasna/connectors",
|
|
11854
|
-
version: "1.
|
|
11854
|
+
version: "1.3.1",
|
|
11855
11855
|
description: "Open source connector library - Install API connectors with a single command",
|
|
11856
11856
|
type: "module",
|
|
11857
11857
|
bin: {
|
package/bin/mcp.js
CHANGED
|
@@ -27082,7 +27082,7 @@ init_strip();
|
|
|
27082
27082
|
// package.json
|
|
27083
27083
|
var package_default = {
|
|
27084
27084
|
name: "@hasna/connectors",
|
|
27085
|
-
version: "1.
|
|
27085
|
+
version: "1.3.1",
|
|
27086
27086
|
description: "Open source connector library - Install API connectors with a single command",
|
|
27087
27087
|
type: "module",
|
|
27088
27088
|
bin: {
|
|
@@ -124,6 +124,11 @@ export class HuggingFaceClient {
|
|
|
124
124
|
return this.request<T>(path, { method: 'DELETE', params });
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
/** Get the raw API key (needed by InferenceApi for direct fetch) */
|
|
128
|
+
getApiKey(): string {
|
|
129
|
+
return this.apiKey;
|
|
130
|
+
}
|
|
131
|
+
|
|
127
132
|
/**
|
|
128
133
|
* Get a preview of the API key (for display/debugging)
|
|
129
134
|
*/
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { HuggingFaceClient } from './client';
|
|
2
|
+
|
|
3
|
+
export interface DatasetSearchOptions {
|
|
4
|
+
search?: string;
|
|
5
|
+
author?: string;
|
|
6
|
+
filter?: string;
|
|
7
|
+
sort?: 'likes' | 'downloads' | 'trending' | 'lastModified';
|
|
8
|
+
direction?: 'asc' | 'desc';
|
|
9
|
+
limit?: number;
|
|
10
|
+
full?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface DatasetInfo {
|
|
14
|
+
_id: string;
|
|
15
|
+
id: string;
|
|
16
|
+
author?: string;
|
|
17
|
+
sha?: string;
|
|
18
|
+
lastModified?: string;
|
|
19
|
+
private?: boolean;
|
|
20
|
+
gated?: boolean | string;
|
|
21
|
+
tags?: string[];
|
|
22
|
+
downloads?: number;
|
|
23
|
+
likes?: number;
|
|
24
|
+
description?: string;
|
|
25
|
+
citation?: string;
|
|
26
|
+
cardData?: Record<string, unknown>;
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface DatasetSplit {
|
|
31
|
+
dataset: string;
|
|
32
|
+
config: string;
|
|
33
|
+
split: string;
|
|
34
|
+
num_rows: number;
|
|
35
|
+
num_bytes: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class DatasetsApi {
|
|
39
|
+
constructor(private readonly client: HuggingFaceClient) {}
|
|
40
|
+
|
|
41
|
+
/** Search/list datasets */
|
|
42
|
+
async search(options: DatasetSearchOptions = {}): Promise<DatasetInfo[]> {
|
|
43
|
+
const params: Record<string, string | number | boolean | undefined> = {};
|
|
44
|
+
if (options.search) params.search = options.search;
|
|
45
|
+
if (options.author) params.author = options.author;
|
|
46
|
+
if (options.filter) params.filter = options.filter;
|
|
47
|
+
if (options.sort) params.sort = options.sort;
|
|
48
|
+
if (options.direction) params.direction = options.direction === 'desc' ? '-1' : '1';
|
|
49
|
+
if (options.limit) params.limit = options.limit;
|
|
50
|
+
if (options.full) params.full = true;
|
|
51
|
+
|
|
52
|
+
return this.client.request<DatasetInfo[]>('/datasets', { params });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Get a single dataset by ID */
|
|
56
|
+
async get(datasetId: string): Promise<DatasetInfo> {
|
|
57
|
+
return this.client.request<DatasetInfo>(`/datasets/${datasetId}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Preview first N rows of a dataset split */
|
|
61
|
+
async preview(datasetId: string, config = 'default', split = 'train', rows = 10): Promise<unknown> {
|
|
62
|
+
// Uses the datasets-server API
|
|
63
|
+
const url = `https://datasets-server.huggingface.co/first-rows?dataset=${encodeURIComponent(datasetId)}&config=${config}&split=${split}&rows=${rows}`;
|
|
64
|
+
const response = await fetch(url, {
|
|
65
|
+
headers: { Authorization: `Bearer ${this.client.getApiKey()}` },
|
|
66
|
+
});
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
const text = await response.text();
|
|
69
|
+
throw new Error(`Dataset preview failed (${response.status}): ${text}`);
|
|
70
|
+
}
|
|
71
|
+
return response.json();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { HuggingFaceConfig } from '../types';
|
|
2
2
|
import { HuggingFaceClient } from './client';
|
|
3
|
-
import {
|
|
3
|
+
import { ModelsApi } from './models';
|
|
4
|
+
import { InferenceApi } from './inference';
|
|
5
|
+
import { DatasetsApi } from './datasets';
|
|
6
|
+
import { SpacesApi } from './spaces';
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
9
|
* Main HuggingFace API class
|
|
@@ -8,42 +11,37 @@ import { ExampleApi } from './example';
|
|
|
8
11
|
export class HuggingFace {
|
|
9
12
|
private readonly client: HuggingFaceClient;
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
public readonly
|
|
14
|
+
public readonly models: ModelsApi;
|
|
15
|
+
public readonly inference: InferenceApi;
|
|
16
|
+
public readonly datasets: DatasetsApi;
|
|
17
|
+
public readonly spaces: SpacesApi;
|
|
13
18
|
|
|
14
19
|
constructor(config: HuggingFaceConfig) {
|
|
15
20
|
this.client = new HuggingFaceClient(config);
|
|
16
|
-
this.
|
|
21
|
+
this.models = new ModelsApi(this.client);
|
|
22
|
+
this.inference = new InferenceApi(this.client);
|
|
23
|
+
this.datasets = new DatasetsApi(this.client);
|
|
24
|
+
this.spaces = new SpacesApi(this.client);
|
|
17
25
|
}
|
|
18
26
|
|
|
19
|
-
/**
|
|
20
|
-
* Create a client from environment variables
|
|
21
|
-
* Looks for HUGGINGFACE_API_KEY or HF_TOKEN
|
|
22
|
-
*/
|
|
23
27
|
static fromEnv(): HuggingFace {
|
|
24
28
|
const apiKey = process.env.HUGGINGFACE_API_KEY || process.env.HF_TOKEN;
|
|
25
29
|
const apiSecret = process.env.HUGGINGFACE_API_SECRET;
|
|
26
|
-
|
|
27
|
-
if (!apiKey) {
|
|
28
|
-
throw new Error('HUGGINGFACE_API_KEY or HF_TOKEN environment variable is required');
|
|
29
|
-
}
|
|
30
|
+
if (!apiKey) throw new Error('HUGGINGFACE_API_KEY or HF_TOKEN environment variable is required');
|
|
30
31
|
return new HuggingFace({ apiKey, apiSecret });
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
/**
|
|
34
|
-
* Get a preview of the API key (for debugging)
|
|
35
|
-
*/
|
|
36
34
|
getApiKeyPreview(): string {
|
|
37
35
|
return this.client.getApiKeyPreview();
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
/**
|
|
41
|
-
* Get the underlying client for direct API access
|
|
42
|
-
*/
|
|
43
38
|
getClient(): HuggingFaceClient {
|
|
44
39
|
return this.client;
|
|
45
40
|
}
|
|
46
41
|
}
|
|
47
42
|
|
|
48
43
|
export { HuggingFaceClient } from './client';
|
|
49
|
-
export {
|
|
44
|
+
export { ModelsApi } from './models';
|
|
45
|
+
export { InferenceApi } from './inference';
|
|
46
|
+
export { DatasetsApi } from './datasets';
|
|
47
|
+
export { SpacesApi } from './spaces';
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { HuggingFaceClient } from './client';
|
|
2
|
+
|
|
3
|
+
const INFERENCE_URL = 'https://api-inference.huggingface.co/models';
|
|
4
|
+
|
|
5
|
+
export interface TextGenerationOptions {
|
|
6
|
+
max_new_tokens?: number;
|
|
7
|
+
temperature?: number;
|
|
8
|
+
top_p?: number;
|
|
9
|
+
top_k?: number;
|
|
10
|
+
repetition_penalty?: number;
|
|
11
|
+
return_full_text?: boolean;
|
|
12
|
+
stop?: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface TextGenerationResult {
|
|
16
|
+
generated_text: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ChatMessage {
|
|
20
|
+
role: 'system' | 'user' | 'assistant';
|
|
21
|
+
content: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ChatCompletionResult {
|
|
25
|
+
choices: Array<{
|
|
26
|
+
message: { role: string; content: string };
|
|
27
|
+
finish_reason: string;
|
|
28
|
+
index: number;
|
|
29
|
+
}>;
|
|
30
|
+
model: string;
|
|
31
|
+
usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class InferenceApi {
|
|
35
|
+
constructor(private readonly client: HuggingFaceClient) {}
|
|
36
|
+
|
|
37
|
+
/** Run text generation inference */
|
|
38
|
+
async textGeneration(
|
|
39
|
+
model: string,
|
|
40
|
+
prompt: string,
|
|
41
|
+
options: TextGenerationOptions = {}
|
|
42
|
+
): Promise<TextGenerationResult[]> {
|
|
43
|
+
const response = await fetch(`${INFERENCE_URL}/${model}`, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: {
|
|
46
|
+
Authorization: `Bearer ${this.client.getApiKey()}`,
|
|
47
|
+
'Content-Type': 'application/json',
|
|
48
|
+
},
|
|
49
|
+
body: JSON.stringify({
|
|
50
|
+
inputs: prompt,
|
|
51
|
+
parameters: {
|
|
52
|
+
max_new_tokens: options.max_new_tokens ?? 256,
|
|
53
|
+
temperature: options.temperature,
|
|
54
|
+
top_p: options.top_p,
|
|
55
|
+
top_k: options.top_k,
|
|
56
|
+
repetition_penalty: options.repetition_penalty,
|
|
57
|
+
return_full_text: options.return_full_text ?? false,
|
|
58
|
+
stop: options.stop,
|
|
59
|
+
},
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
const text = await response.text();
|
|
65
|
+
throw new Error(`Inference failed (${response.status}): ${text}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return response.json();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Chat completion via HF Inference API (for chat models) */
|
|
72
|
+
async chat(
|
|
73
|
+
model: string,
|
|
74
|
+
messages: ChatMessage[],
|
|
75
|
+
options: Omit<TextGenerationOptions, 'return_full_text'> = {}
|
|
76
|
+
): Promise<ChatCompletionResult> {
|
|
77
|
+
const response = await fetch(`${INFERENCE_URL}/${model}/v1/chat/completions`, {
|
|
78
|
+
method: 'POST',
|
|
79
|
+
headers: {
|
|
80
|
+
Authorization: `Bearer ${this.client.getApiKey()}`,
|
|
81
|
+
'Content-Type': 'application/json',
|
|
82
|
+
},
|
|
83
|
+
body: JSON.stringify({
|
|
84
|
+
model,
|
|
85
|
+
messages,
|
|
86
|
+
max_tokens: options.max_new_tokens ?? 256,
|
|
87
|
+
temperature: options.temperature,
|
|
88
|
+
top_p: options.top_p,
|
|
89
|
+
stop: options.stop,
|
|
90
|
+
}),
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
const text = await response.text();
|
|
95
|
+
throw new Error(`Chat inference failed (${response.status}): ${text}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return response.json();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { HuggingFaceClient } from './client';
|
|
2
|
+
|
|
3
|
+
export interface ModelSearchOptions {
|
|
4
|
+
search?: string;
|
|
5
|
+
author?: string;
|
|
6
|
+
filter?: string; // task filter: text-generation, text2text-generation, etc
|
|
7
|
+
library?: string; // transformers, gguf, pytorch, etc
|
|
8
|
+
sort?: 'likes' | 'downloads' | 'trending' | 'lastModified';
|
|
9
|
+
direction?: 'asc' | 'desc';
|
|
10
|
+
limit?: number;
|
|
11
|
+
full?: boolean; // include all fields
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ModelInfo {
|
|
15
|
+
_id: string;
|
|
16
|
+
id: string; // e.g. "meta-llama/Meta-Llama-3-8B"
|
|
17
|
+
modelId: string;
|
|
18
|
+
author?: string;
|
|
19
|
+
sha?: string;
|
|
20
|
+
lastModified?: string;
|
|
21
|
+
private?: boolean;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
gated?: boolean | string;
|
|
24
|
+
pipeline_tag?: string;
|
|
25
|
+
tags?: string[];
|
|
26
|
+
downloads?: number;
|
|
27
|
+
likes?: number;
|
|
28
|
+
library_name?: string;
|
|
29
|
+
[key: string]: unknown;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ModelFile {
|
|
33
|
+
rfilename: string;
|
|
34
|
+
size?: number;
|
|
35
|
+
blobId?: string;
|
|
36
|
+
lfs?: { size: number; sha256: string; pointerSize: number };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class ModelsApi {
|
|
40
|
+
constructor(private readonly client: HuggingFaceClient) {}
|
|
41
|
+
|
|
42
|
+
/** Search/list models */
|
|
43
|
+
async search(options: ModelSearchOptions = {}): Promise<ModelInfo[]> {
|
|
44
|
+
const params: Record<string, string | number | boolean | undefined> = {};
|
|
45
|
+
if (options.search) params.search = options.search;
|
|
46
|
+
if (options.author) params.author = options.author;
|
|
47
|
+
if (options.filter) params.filter = options.filter;
|
|
48
|
+
if (options.library) params.library = options.library;
|
|
49
|
+
if (options.sort) params.sort = options.sort;
|
|
50
|
+
if (options.direction) params.direction = options.direction === 'desc' ? '-1' : '1';
|
|
51
|
+
if (options.limit) params.limit = options.limit;
|
|
52
|
+
if (options.full) params.full = true;
|
|
53
|
+
|
|
54
|
+
return this.client.request<ModelInfo[]>('/models', { params });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Get a single model by ID (e.g. "meta-llama/Meta-Llama-3-8B") */
|
|
58
|
+
async get(modelId: string): Promise<ModelInfo> {
|
|
59
|
+
return this.client.request<ModelInfo>(`/models/${modelId}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** List files in a model repo */
|
|
63
|
+
async files(modelId: string): Promise<ModelFile[]> {
|
|
64
|
+
return this.client.request<ModelFile[]>(`/models/${modelId}/tree/main`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { HuggingFaceClient } from './client';
|
|
2
|
+
|
|
3
|
+
export interface SpaceSearchOptions {
|
|
4
|
+
search?: string;
|
|
5
|
+
author?: string;
|
|
6
|
+
sort?: 'likes' | 'trending' | 'lastModified';
|
|
7
|
+
direction?: 'asc' | 'desc';
|
|
8
|
+
limit?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface SpaceInfo {
|
|
12
|
+
_id: string;
|
|
13
|
+
id: string;
|
|
14
|
+
author?: string;
|
|
15
|
+
sha?: string;
|
|
16
|
+
lastModified?: string;
|
|
17
|
+
private?: boolean;
|
|
18
|
+
tags?: string[];
|
|
19
|
+
likes?: number;
|
|
20
|
+
sdk?: string;
|
|
21
|
+
runtime?: { stage: string; hardware?: { current?: string } };
|
|
22
|
+
[key: string]: unknown;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class SpacesApi {
|
|
26
|
+
constructor(private readonly client: HuggingFaceClient) {}
|
|
27
|
+
|
|
28
|
+
async search(options: SpaceSearchOptions = {}): Promise<SpaceInfo[]> {
|
|
29
|
+
const params: Record<string, string | number | boolean | undefined> = {};
|
|
30
|
+
if (options.search) params.search = options.search;
|
|
31
|
+
if (options.author) params.author = options.author;
|
|
32
|
+
if (options.sort) params.sort = options.sort;
|
|
33
|
+
if (options.direction) params.direction = options.direction === 'desc' ? '-1' : '1';
|
|
34
|
+
if (options.limit) params.limit = options.limit;
|
|
35
|
+
|
|
36
|
+
return this.client.request<SpaceInfo[]>('/spaces', { params });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async get(spaceId: string): Promise<SpaceInfo> {
|
|
40
|
+
return this.client.request<SpaceInfo>(`/spaces/${spaceId}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -188,55 +188,167 @@ configCmd
|
|
|
188
188
|
});
|
|
189
189
|
|
|
190
190
|
// ============================================
|
|
191
|
-
//
|
|
191
|
+
// Models Commands
|
|
192
192
|
// ============================================
|
|
193
|
-
const
|
|
194
|
-
.command('example')
|
|
195
|
-
.description('Example API commands (replace with HuggingFace commands)');
|
|
193
|
+
const modelsCmd = program.command('models').description('Search and browse HuggingFace models');
|
|
196
194
|
|
|
197
|
-
|
|
198
|
-
.command('
|
|
199
|
-
.description('
|
|
200
|
-
.
|
|
201
|
-
.
|
|
195
|
+
modelsCmd
|
|
196
|
+
.command('search')
|
|
197
|
+
.description('Search models')
|
|
198
|
+
.argument('[query]', 'Search query')
|
|
199
|
+
.option('--task <task>', 'Filter by task (text-generation, text2text-generation, etc)')
|
|
200
|
+
.option('--library <lib>', 'Filter by library (transformers, gguf, pytorch)')
|
|
201
|
+
.option('--author <author>', 'Filter by author')
|
|
202
|
+
.option('--sort <field>', 'Sort by: likes, downloads, trending, lastModified', 'trending')
|
|
203
|
+
.option('--limit <n>', 'Max results', '20')
|
|
204
|
+
.action(async (query, opts) => {
|
|
202
205
|
try {
|
|
203
206
|
const client = getClient();
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
207
|
+
const results = await client.models.search({
|
|
208
|
+
search: query, filter: opts.task, library: opts.library,
|
|
209
|
+
author: opts.author, sort: opts.sort, limit: parseInt(opts.limit),
|
|
210
|
+
});
|
|
211
|
+
print(results.map(m => ({ id: m.id, task: m.pipeline_tag, library: m.library_name, downloads: m.downloads, likes: m.likes })), getFormat(modelsCmd));
|
|
212
|
+
} catch (err) { error(String(err)); process.exit(1); }
|
|
210
213
|
});
|
|
211
214
|
|
|
212
|
-
|
|
215
|
+
modelsCmd
|
|
213
216
|
.command('get <id>')
|
|
214
|
-
.description('Get
|
|
215
|
-
.action(async (id
|
|
217
|
+
.description('Get model details (e.g. meta-llama/Meta-Llama-3-8B)')
|
|
218
|
+
.action(async (id) => {
|
|
216
219
|
try {
|
|
217
220
|
const client = getClient();
|
|
218
|
-
const result = await client.
|
|
219
|
-
print(result, getFormat(
|
|
220
|
-
} catch (err) {
|
|
221
|
-
error(String(err));
|
|
222
|
-
process.exit(1);
|
|
223
|
-
}
|
|
221
|
+
const result = await client.models.get(id);
|
|
222
|
+
print(result, getFormat(modelsCmd));
|
|
223
|
+
} catch (err) { error(String(err)); process.exit(1); }
|
|
224
224
|
});
|
|
225
225
|
|
|
226
|
-
|
|
227
|
-
.command('
|
|
228
|
-
.description('
|
|
229
|
-
.
|
|
230
|
-
.action(async (opts) => {
|
|
226
|
+
modelsCmd
|
|
227
|
+
.command('files <id>')
|
|
228
|
+
.description('List files in a model repo')
|
|
229
|
+
.action(async (id) => {
|
|
231
230
|
try {
|
|
232
231
|
const client = getClient();
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
232
|
+
const files = await client.models.files(id);
|
|
233
|
+
print(files.map(f => ({ name: f.rfilename, size: f.lfs?.size ?? f.size ?? null })), getFormat(modelsCmd));
|
|
234
|
+
} catch (err) { error(String(err)); process.exit(1); }
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// ============================================
|
|
238
|
+
// Inference Commands
|
|
239
|
+
// ============================================
|
|
240
|
+
const inferCmd = program.command('inference').description('Run model inference via HF Inference API');
|
|
241
|
+
|
|
242
|
+
inferCmd
|
|
243
|
+
.command('text-generation <model>')
|
|
244
|
+
.description('Generate text from a prompt')
|
|
245
|
+
.requiredOption('--prompt <text>', 'Input prompt')
|
|
246
|
+
.option('--max-tokens <n>', 'Max new tokens', '256')
|
|
247
|
+
.option('--temperature <t>', 'Temperature', '0.7')
|
|
248
|
+
.action(async (model, opts) => {
|
|
249
|
+
try {
|
|
250
|
+
const client = getClient();
|
|
251
|
+
const results = await client.inference.textGeneration(model, opts.prompt, {
|
|
252
|
+
max_new_tokens: parseInt(opts.maxTokens), temperature: parseFloat(opts.temperature),
|
|
253
|
+
});
|
|
254
|
+
print(results, getFormat(inferCmd));
|
|
255
|
+
} catch (err) { error(String(err)); process.exit(1); }
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
inferCmd
|
|
259
|
+
.command('chat <model>')
|
|
260
|
+
.description('Chat completion (for chat models)')
|
|
261
|
+
.requiredOption('--messages <json>', 'Messages JSON array')
|
|
262
|
+
.option('--max-tokens <n>', 'Max new tokens', '256')
|
|
263
|
+
.option('--temperature <t>', 'Temperature', '0.7')
|
|
264
|
+
.action(async (model, opts) => {
|
|
265
|
+
try {
|
|
266
|
+
const messages = JSON.parse(opts.messages);
|
|
267
|
+
const client = getClient();
|
|
268
|
+
const result = await client.inference.chat(model, messages, {
|
|
269
|
+
max_new_tokens: parseInt(opts.maxTokens), temperature: parseFloat(opts.temperature),
|
|
270
|
+
});
|
|
271
|
+
print(result, getFormat(inferCmd));
|
|
272
|
+
} catch (err) { error(String(err)); process.exit(1); }
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// ============================================
|
|
276
|
+
// Datasets Commands
|
|
277
|
+
// ============================================
|
|
278
|
+
const datasetsCmd = program.command('datasets').description('Search and browse HuggingFace datasets');
|
|
279
|
+
|
|
280
|
+
datasetsCmd
|
|
281
|
+
.command('search')
|
|
282
|
+
.description('Search datasets')
|
|
283
|
+
.argument('[query]', 'Search query')
|
|
284
|
+
.option('--author <author>', 'Filter by author')
|
|
285
|
+
.option('--sort <field>', 'Sort by: likes, downloads, trending', 'trending')
|
|
286
|
+
.option('--limit <n>', 'Max results', '20')
|
|
287
|
+
.action(async (query, opts) => {
|
|
288
|
+
try {
|
|
289
|
+
const client = getClient();
|
|
290
|
+
const results = await client.datasets.search({
|
|
291
|
+
search: query, author: opts.author, sort: opts.sort, limit: parseInt(opts.limit),
|
|
292
|
+
});
|
|
293
|
+
print(results.map(d => ({ id: d.id, downloads: d.downloads, likes: d.likes, tags: d.tags?.slice(0, 5) })), getFormat(datasetsCmd));
|
|
294
|
+
} catch (err) { error(String(err)); process.exit(1); }
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
datasetsCmd
|
|
298
|
+
.command('get <id>')
|
|
299
|
+
.description('Get dataset details')
|
|
300
|
+
.action(async (id) => {
|
|
301
|
+
try {
|
|
302
|
+
const client = getClient();
|
|
303
|
+
const result = await client.datasets.get(id);
|
|
304
|
+
print(result, getFormat(datasetsCmd));
|
|
305
|
+
} catch (err) { error(String(err)); process.exit(1); }
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
datasetsCmd
|
|
309
|
+
.command('preview <id>')
|
|
310
|
+
.description('Preview first N rows of a dataset')
|
|
311
|
+
.option('--split <split>', 'Dataset split', 'train')
|
|
312
|
+
.option('--rows <n>', 'Number of rows', '10')
|
|
313
|
+
.action(async (id, opts) => {
|
|
314
|
+
try {
|
|
315
|
+
const client = getClient();
|
|
316
|
+
const result = await client.datasets.preview(id, 'default', opts.split, parseInt(opts.rows));
|
|
317
|
+
print(result, getFormat(datasetsCmd));
|
|
318
|
+
} catch (err) { error(String(err)); process.exit(1); }
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// ============================================
|
|
322
|
+
// Spaces Commands
|
|
323
|
+
// ============================================
|
|
324
|
+
const spacesCmd = program.command('spaces').description('Search and browse HuggingFace Spaces');
|
|
325
|
+
|
|
326
|
+
spacesCmd
|
|
327
|
+
.command('search')
|
|
328
|
+
.description('Search spaces')
|
|
329
|
+
.argument('[query]', 'Search query')
|
|
330
|
+
.option('--author <author>', 'Filter by author')
|
|
331
|
+
.option('--sort <field>', 'Sort by: likes, trending', 'trending')
|
|
332
|
+
.option('--limit <n>', 'Max results', '20')
|
|
333
|
+
.action(async (query, opts) => {
|
|
334
|
+
try {
|
|
335
|
+
const client = getClient();
|
|
336
|
+
const results = await client.spaces.search({
|
|
337
|
+
search: query, author: opts.author, sort: opts.sort, limit: parseInt(opts.limit),
|
|
338
|
+
});
|
|
339
|
+
print(results.map(s => ({ id: s.id, sdk: s.sdk, likes: s.likes })), getFormat(spacesCmd));
|
|
340
|
+
} catch (err) { error(String(err)); process.exit(1); }
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
spacesCmd
|
|
344
|
+
.command('get <id>')
|
|
345
|
+
.description('Get space details')
|
|
346
|
+
.action(async (id) => {
|
|
347
|
+
try {
|
|
348
|
+
const client = getClient();
|
|
349
|
+
const result = await client.spaces.get(id);
|
|
350
|
+
print(result, getFormat(spacesCmd));
|
|
351
|
+
} catch (err) { error(String(err)); process.exit(1); }
|
|
240
352
|
});
|
|
241
353
|
|
|
242
354
|
// Parse and execute
|
|
@@ -5,8 +5,20 @@ import type {
|
|
|
5
5
|
ImageOptions,
|
|
6
6
|
} from '../types';
|
|
7
7
|
|
|
8
|
+
/** Models that use the new gpt-image API (different params than DALL-E) */
|
|
9
|
+
const GPT_IMAGE_MODELS = ['gpt-image-1'];
|
|
10
|
+
|
|
11
|
+
function isGptImage(model: string): boolean {
|
|
12
|
+
return GPT_IMAGE_MODELS.some((m) => model.startsWith(m));
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
/**
|
|
9
|
-
* Images API
|
|
16
|
+
* Images API — supports both DALL-E 3 and gpt-image-1
|
|
17
|
+
*
|
|
18
|
+
* gpt-image-1 differences:
|
|
19
|
+
* - Uses `output_format` instead of `response_format`
|
|
20
|
+
* - Does NOT support `style` parameter
|
|
21
|
+
* - Does NOT support `response_format` parameter
|
|
10
22
|
*/
|
|
11
23
|
export class ImagesApi {
|
|
12
24
|
constructor(private readonly client: OpenAIClient) {}
|
|
@@ -18,13 +30,29 @@ export class ImagesApi {
|
|
|
18
30
|
prompt: string,
|
|
19
31
|
options: ImageOptions = {}
|
|
20
32
|
): Promise<ImageResponse> {
|
|
33
|
+
const model = options.model || 'dall-e-3';
|
|
34
|
+
|
|
35
|
+
if (isGptImage(model)) {
|
|
36
|
+
// gpt-image-1: different parameter set
|
|
37
|
+
const request: Record<string, unknown> = {
|
|
38
|
+
model,
|
|
39
|
+
prompt,
|
|
40
|
+
n: options.n || 1,
|
|
41
|
+
};
|
|
42
|
+
if (options.size !== undefined) request.size = options.size;
|
|
43
|
+
if (options.quality !== undefined) request.quality = options.quality;
|
|
44
|
+
// gpt-image-1 uses output_format, not response_format. No style param.
|
|
45
|
+
request.output_format = 'url';
|
|
46
|
+
return this.client.post<ImageResponse>('/images/generations', request);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// DALL-E 3: original parameter set
|
|
21
50
|
const request: ImageGenerateRequest = {
|
|
22
|
-
model
|
|
51
|
+
model,
|
|
23
52
|
prompt,
|
|
24
53
|
n: options.n || 1,
|
|
25
54
|
response_format: 'url',
|
|
26
55
|
};
|
|
27
|
-
|
|
28
56
|
if (options.size !== undefined) request.size = options.size;
|
|
29
57
|
if (options.quality !== undefined) request.quality = options.quality;
|
|
30
58
|
if (options.style !== undefined) request.style = options.style;
|
|
@@ -54,22 +82,38 @@ export class ImagesApi {
|
|
|
54
82
|
prompt: string,
|
|
55
83
|
options: Omit<ImageOptions, 'n'> = {}
|
|
56
84
|
): Promise<string> {
|
|
85
|
+
const model = options.model || 'dall-e-3';
|
|
86
|
+
|
|
87
|
+
if (isGptImage(model)) {
|
|
88
|
+
const request: Record<string, unknown> = {
|
|
89
|
+
model,
|
|
90
|
+
prompt,
|
|
91
|
+
n: 1,
|
|
92
|
+
output_format: 'b64_json',
|
|
93
|
+
};
|
|
94
|
+
if (options.size !== undefined) request.size = options.size;
|
|
95
|
+
if (options.quality !== undefined) request.quality = options.quality;
|
|
96
|
+
|
|
97
|
+
const response = await this.client.post<ImageResponse>('/images/generations', request);
|
|
98
|
+
const b64 = response.data[0]?.b64_json;
|
|
99
|
+
if (!b64) throw new Error('No image data in response');
|
|
100
|
+
return b64;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// DALL-E 3
|
|
57
104
|
const request: ImageGenerateRequest = {
|
|
58
|
-
model
|
|
105
|
+
model,
|
|
59
106
|
prompt,
|
|
60
107
|
n: 1,
|
|
61
108
|
response_format: 'b64_json',
|
|
62
109
|
};
|
|
63
|
-
|
|
64
110
|
if (options.size !== undefined) request.size = options.size;
|
|
65
111
|
if (options.quality !== undefined) request.quality = options.quality;
|
|
66
112
|
if (options.style !== undefined) request.style = options.style;
|
|
67
113
|
|
|
68
114
|
const response = await this.client.post<ImageResponse>('/images/generations', request);
|
|
69
115
|
const b64 = response.data[0]?.b64_json;
|
|
70
|
-
if (!b64)
|
|
71
|
-
throw new Error('No image data in response');
|
|
72
|
-
}
|
|
116
|
+
if (!b64) throw new Error('No image data in response');
|
|
73
117
|
return b64;
|
|
74
118
|
}
|
|
75
119
|
}
|
package/package.json
CHANGED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import type { HuggingFaceClient } from './client';
|
|
2
|
-
import type { ExampleResource, ExampleListResponse, ExampleCreateParams } from '../types';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Example API module - demonstrates the pattern for API modules
|
|
6
|
-
* Replace with actual HuggingFace API endpoints (models, datasets, spaces, etc.)
|
|
7
|
-
*/
|
|
8
|
-
export class ExampleApi {
|
|
9
|
-
constructor(private readonly client: HuggingFaceClient) {}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* List resources with optional pagination
|
|
13
|
-
*/
|
|
14
|
-
async list(options?: { maxResults?: number; pageToken?: string }): Promise<ExampleListResponse> {
|
|
15
|
-
return this.client.get<ExampleListResponse>('/resources', {
|
|
16
|
-
max_results: options?.maxResults,
|
|
17
|
-
page_token: options?.pageToken,
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Get a single resource by ID
|
|
23
|
-
*/
|
|
24
|
-
async get(id: string): Promise<ExampleResource> {
|
|
25
|
-
return this.client.get<ExampleResource>(`/resources/${id}`);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Create a new resource
|
|
30
|
-
*/
|
|
31
|
-
async create(params: ExampleCreateParams): Promise<ExampleResource> {
|
|
32
|
-
return this.client.post<ExampleResource>('/resources', params);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Update an existing resource
|
|
37
|
-
*/
|
|
38
|
-
async update(id: string, params: Partial<ExampleCreateParams>): Promise<ExampleResource> {
|
|
39
|
-
return this.client.patch<ExampleResource>(`/resources/${id}`, params);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Delete a resource
|
|
44
|
-
*/
|
|
45
|
-
async delete(id: string): Promise<void> {
|
|
46
|
-
await this.client.delete(`/resources/${id}`);
|
|
47
|
-
}
|
|
48
|
-
}
|