Skip to content

Commit b20d7c5

Browse files
authored
Load tiny-agents from Hub (#1472)
As discussed offline, this PR moves the curated collection of tiny-agents to the Hub. Agents must now be contributed to https://huggingface.co/datasets/huggingface/tiny-agents. This will be helpful to maintain the collection independently from the `@huggingface/tiny-agents` releases. It also makes them available for the incoming Python equivalent (huggingface/huggingface_hub#3098). File resolution is still the same: 1. if agent_id is a file => load it as a `agent.json` file 2. if agent_id is a directory => load from it 3. if agent_id is a subdirectory in https://huggingface.co/datasets/huggingface/tiny-agents/tree/main => load from it 4. otherwise raise exception Usage is still: ```bash npx @huggingface/tiny-agents run "julien-c/flux-schnell-generator" ```
1 parent 6ba8619 commit b20d7c5

File tree

13 files changed

+126
-121
lines changed

13 files changed

+126
-121
lines changed

packages/tiny-agents/README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,19 @@ npx @huggingface/tiny-agents run ./my-agent
8585

8686
Voilà! 🔥
8787

88+
89+
## Tiny Agents collection
90+
91+
Browse our curated collection of Tiny Agents at https://huggingface.co/datasets/tiny-agents/tiny-agents. Each agent is stored in its own subdirectory, following the structure outlined above. Running an agent from the Hub is as simple as using its `agent_id`. For example, to run the [`julien-c/flux-schnell-generator`](https://huggingface.co/datasets/tiny-agents/tiny-agents/tree/main/julien-c/flux-schnell-generator) agent:
92+
93+
```bash
94+
npx @huggingface/tiny-agents run "julien-c/flux-schnell-generator"
95+
```
96+
8897
> [!NOTE]
89-
> Note: you can open a PR in the huggingface.js repo to share your agent with the community, just upload it inside the `src/agents/` directory.
98+
> Want to share your own agent with the community? Submit a PR to the [Tiny Agents](https://huggingface.co/datasets/tiny-agents/tiny-agents/discussions) repository on the Hub. Your submission must include an `agent.json` file, and you can optionally add a `PROMPT.md` file. To help others understand your agent's capabilities, consider including an `EXAMPLES.md` file with sample prompts and use cases.
9099
91-
### Advanced: Programmatic Usage
100+
## Advanced: Programmatic Usage
92101

93102
```typescript
94103
import { Agent } from '@huggingface/tiny-agents';

packages/tiny-agents/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
"@huggingface/inference": "workspace:^",
5555
"@huggingface/mcp-client": "workspace:^",
5656
"@huggingface/tasks": "workspace:^",
57+
"@huggingface/hub": "workspace:^",
5758
"@modelcontextprotocol/sdk": "^1.11.4",
5859
"zod": "^3.25.7"
5960
}

packages/tiny-agents/pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/tiny-agents/src/agents/evalstate/hf-search/PROMPT.md

Lines changed: 0 additions & 7 deletions
This file was deleted.

packages/tiny-agents/src/agents/evalstate/hf-search/agent.json

Lines changed: 0 additions & 12 deletions
This file was deleted.

packages/tiny-agents/src/agents/evalstate/hf.js-assistant/PROMPT.md

Lines changed: 0 additions & 7 deletions
This file was deleted.

packages/tiny-agents/src/agents/evalstate/hf.js-assistant/agent.json

Lines changed: 0 additions & 12 deletions
This file was deleted.

packages/tiny-agents/src/agents/julien-c/flux-schnell-generator/agent.json

Lines changed: 0 additions & 12 deletions
This file was deleted.

packages/tiny-agents/src/agents/julien-c/local-coder/PROMPT.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

packages/tiny-agents/src/agents/julien-c/local-coder/agent.json

Lines changed: 0 additions & 13 deletions
This file was deleted.

packages/tiny-agents/src/cli.ts

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
#!/usr/bin/env node
2-
import { dirname, join } from "node:path";
32
import { parseArgs } from "node:util";
4-
import { lstat, readFile } from "node:fs/promises";
53
import { z } from "zod";
64
import { PROVIDERS_OR_POLICIES } from "@huggingface/inference";
75
import { Agent } from "@huggingface/mcp-client";
86
import { version as packageVersion } from "../package.json";
97
import { ServerConfigSchema } from "./lib/types";
108
import { debug, error } from "./lib/utils";
119
import { mainCliLoop } from "./lib/mainCliLoop";
10+
import { loadConfigFrom } from "./lib/loadConfigFrom";
1211

1312
const USAGE_HELP = `
1413
Usage:
@@ -30,55 +29,6 @@ function isValidCommand(command: string): command is (typeof CLI_COMMANDS)[numbe
3029
return (CLI_COMMANDS as unknown as string[]).includes(command);
3130
}
3231

33-
const FILENAME_CONFIG = "agent.json";
34-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
35-
const FILENAME_PROMPT = "PROMPT.md";
36-
37-
async function loadConfigFrom(loadFrom: string): Promise<{ configJson: string; prompt?: string }> {
38-
try {
39-
/// First try it as a local file path, then as a local directory, then we will try as a path inside the repo itself
40-
return {
41-
configJson: await readFile(loadFrom, { encoding: "utf8" }),
42-
};
43-
} catch {
44-
if ((await lstat(loadFrom)).isDirectory()) {
45-
/// local directory
46-
try {
47-
let prompt: string | undefined;
48-
try {
49-
prompt = await readFile(join(loadFrom, FILENAME_PROMPT), { encoding: "utf8" });
50-
} catch {
51-
debug(`PROMPT.md not found in ${loadFrom}, continuing without prompt template`);
52-
}
53-
return {
54-
configJson: await readFile(join(loadFrom, FILENAME_CONFIG), { encoding: "utf8" }),
55-
prompt,
56-
};
57-
} catch {
58-
error(`Config file not found in specified local directory.`);
59-
process.exit(1);
60-
}
61-
}
62-
const srcDir = dirname(__filename);
63-
const configDir = join(srcDir, "agents", loadFrom);
64-
try {
65-
let prompt: string | undefined;
66-
try {
67-
prompt = await readFile(join(configDir, FILENAME_PROMPT), { encoding: "utf8" });
68-
} catch {
69-
debug(`PROMPT.md not found in ${configDir}, continuing without prompt template`);
70-
}
71-
return {
72-
configJson: await readFile(join(configDir, FILENAME_CONFIG), { encoding: "utf8" }),
73-
prompt,
74-
};
75-
} catch {
76-
error(`Config file not found in tiny-agents repo! Loading from the HF Hub is not implemented yet`);
77-
process.exit(1);
78-
}
79-
}
80-
}
81-
8232
async function main() {
8333
const {
8434
values: { help, version },
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#!/usr/bin/env node
2+
import { join } from "node:path";
3+
import { lstat, readFile } from "node:fs/promises";
4+
import { downloadFileToCacheDir, type RepoDesignation } from "@huggingface/hub";
5+
import type { TinyAgentConfig } from "./types";
6+
import { debug, error } from "./utils";
7+
8+
const FILENAME_CONFIG = "agent.json";
9+
const FILENAME_PROMPT = "PROMPT.md";
10+
11+
const TINY_AGENTS_HUB_REPO: RepoDesignation = {
12+
name: "tiny-agents/tiny-agents",
13+
type: "dataset",
14+
};
15+
16+
async function tryLoadFromFile(filePath: string): Promise<TinyAgentConfig | undefined> {
17+
try {
18+
const configJson = await readFile(filePath, { encoding: "utf8" });
19+
return { configJson };
20+
} catch {
21+
return undefined;
22+
}
23+
}
24+
25+
async function tryLoadFromDirectory(dirPath: string): Promise<TinyAgentConfig | undefined> {
26+
const stats = await lstat(dirPath).catch(() => undefined);
27+
if (!stats?.isDirectory()) {
28+
return undefined;
29+
}
30+
31+
let prompt: string | undefined;
32+
try {
33+
prompt = await readFile(join(dirPath, FILENAME_PROMPT), { encoding: "utf8" });
34+
} catch {
35+
debug(`PROMPT.md not found in ${dirPath}, continuing without prompt template`);
36+
}
37+
38+
try {
39+
return {
40+
configJson: await readFile(join(dirPath, FILENAME_CONFIG), { encoding: "utf8" }),
41+
prompt,
42+
};
43+
} catch {
44+
error(`Config file not found in specified local directory.`);
45+
process.exit(1);
46+
}
47+
}
48+
49+
async function tryLoadFromHub(agentId: string): Promise<TinyAgentConfig | undefined> {
50+
let configJson: string;
51+
try {
52+
const configPath = await downloadFileToCacheDir({
53+
repo: TINY_AGENTS_HUB_REPO,
54+
path: `${agentId}/${FILENAME_CONFIG}`,
55+
accessToken: process.env.HF_TOKEN,
56+
});
57+
configJson = await readFile(configPath, { encoding: "utf8" });
58+
} catch {
59+
return undefined;
60+
}
61+
62+
let prompt: string | undefined;
63+
try {
64+
const promptPath = await downloadFileToCacheDir({
65+
repo: TINY_AGENTS_HUB_REPO,
66+
path: `${agentId}/${FILENAME_PROMPT}`,
67+
accessToken: process.env.HF_TOKEN,
68+
});
69+
prompt = await readFile(promptPath, { encoding: "utf8" });
70+
} catch {
71+
debug(
72+
`PROMPT.md not found in https://huggingface.co/datasets/tiny-agents/tiny-agents/tree/main/${agentId}, continuing without prompt template`
73+
);
74+
}
75+
76+
return {
77+
configJson,
78+
prompt,
79+
};
80+
}
81+
82+
export async function loadConfigFrom(loadFrom: string): Promise<TinyAgentConfig> {
83+
// First try as a local file
84+
const fileConfig = await tryLoadFromFile(loadFrom);
85+
if (fileConfig) {
86+
return fileConfig;
87+
}
88+
89+
// Then try as a local directory
90+
const dirConfig = await tryLoadFromDirectory(loadFrom);
91+
if (dirConfig) {
92+
return dirConfig;
93+
}
94+
95+
// Finally try loading from repo
96+
const repoConfig = await tryLoadFromHub(loadFrom);
97+
if (repoConfig) {
98+
return repoConfig;
99+
}
100+
101+
error(
102+
`Config file not found in tiny-agents! Please make sure it exists locally or in https://huggingface.co/datasets/tiny-agents/tiny-agents.`
103+
);
104+
process.exit(1);
105+
}

packages/tiny-agents/src/lib/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { z } from "zod";
22

3+
export interface TinyAgentConfig {
4+
configJson: string;
5+
prompt?: string;
6+
}
7+
38
export const ServerConfigSchema = z.discriminatedUnion("type", [
49
z.object({
510
type: z.literal("stdio"),

0 commit comments

Comments
 (0)