diff --git a/packages/tiny-agents/README.md b/packages/tiny-agents/README.md index 487cb48023..cc15e028bc 100644 --- a/packages/tiny-agents/README.md +++ b/packages/tiny-agents/README.md @@ -85,10 +85,19 @@ npx @huggingface/tiny-agents run ./my-agent Voilà! 🔥 + +## Tiny Agents collection + +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: + +```bash +npx @huggingface/tiny-agents run "julien-c/flux-schnell-generator" +``` + > [!NOTE] -> 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. +> 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. -### Advanced: Programmatic Usage +## Advanced: Programmatic Usage ```typescript import { Agent } from '@huggingface/tiny-agents'; diff --git a/packages/tiny-agents/package.json b/packages/tiny-agents/package.json index 39df6c0135..29a9942b1d 100644 --- a/packages/tiny-agents/package.json +++ b/packages/tiny-agents/package.json @@ -54,6 +54,7 @@ "@huggingface/inference": "workspace:^", "@huggingface/mcp-client": "workspace:^", "@huggingface/tasks": "workspace:^", + "@huggingface/hub": "workspace:^", "@modelcontextprotocol/sdk": "^1.11.4", "zod": "^3.25.7" } diff --git a/packages/tiny-agents/pnpm-lock.yaml b/packages/tiny-agents/pnpm-lock.yaml index 3e9aca9ca8..7f76c0f22c 100644 --- a/packages/tiny-agents/pnpm-lock.yaml +++ b/packages/tiny-agents/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@huggingface/hub': + specifier: workspace:^ + version: link:../hub '@huggingface/inference': specifier: workspace:^ version: link:../inference diff --git a/packages/tiny-agents/src/agents/evalstate/hf-search/PROMPT.md b/packages/tiny-agents/src/agents/evalstate/hf-search/PROMPT.md deleted file mode 100644 index 6691100959..0000000000 --- a/packages/tiny-agents/src/agents/evalstate/hf-search/PROMPT.md +++ /dev/null @@ -1,7 +0,0 @@ -You are an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved, or if you need more info from the user to solve the problem. - -If you are not sure about anything pertaining to the user’s request, use your tools to read files and gather the relevant information: do NOT guess or make up an answer. - -You MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully. - -Help the User find relevant Papers, Models and Spaces (which are hosted, running Models accesible via a User Interface) to aid them with their Machine Learning research. Paper IDs are arXiv identifiers, and are commonly referenced between Papers and Models. diff --git a/packages/tiny-agents/src/agents/evalstate/hf-search/agent.json b/packages/tiny-agents/src/agents/evalstate/hf-search/agent.json deleted file mode 100644 index 7e07596f7f..0000000000 --- a/packages/tiny-agents/src/agents/evalstate/hf-search/agent.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "model": "Qwen/Qwen2.5-72B-Instruct", - "provider": "nebius", - "servers": [ - { - "type": "http", - "config": { - "url": "https://evalstate-hf-mcp-server.hf.space/mcp" - } - } - ] -} diff --git a/packages/tiny-agents/src/agents/evalstate/hf.js-assistant/PROMPT.md b/packages/tiny-agents/src/agents/evalstate/hf.js-assistant/PROMPT.md deleted file mode 100644 index b740872819..0000000000 --- a/packages/tiny-agents/src/agents/evalstate/hf.js-assistant/PROMPT.md +++ /dev/null @@ -1,7 +0,0 @@ -You are an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved, or if you need more info from the user to solve the problem. - -If you are not sure about anything pertaining to the user’s request, use your tools to read files and gather the relevant information: do NOT guess or make up an answer. - -You MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully. - -You have access to tools to help you answer questions the User has about the "huggingface.js" repository. The repository contains a number of packages for working with the Hugging Face APIs, Tools and Services. This includes API search functions, inference libraries, LLM Clients and MCP, Agents. diff --git a/packages/tiny-agents/src/agents/evalstate/hf.js-assistant/agent.json b/packages/tiny-agents/src/agents/evalstate/hf.js-assistant/agent.json deleted file mode 100644 index 7e07596f7f..0000000000 --- a/packages/tiny-agents/src/agents/evalstate/hf.js-assistant/agent.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "model": "Qwen/Qwen2.5-72B-Instruct", - "provider": "nebius", - "servers": [ - { - "type": "http", - "config": { - "url": "https://evalstate-hf-mcp-server.hf.space/mcp" - } - } - ] -} diff --git a/packages/tiny-agents/src/agents/julien-c/flux-schnell-generator/agent.json b/packages/tiny-agents/src/agents/julien-c/flux-schnell-generator/agent.json deleted file mode 100644 index fa0c26c148..0000000000 --- a/packages/tiny-agents/src/agents/julien-c/flux-schnell-generator/agent.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "model": "Qwen/Qwen2.5-72B-Instruct", - "provider": "nebius", - "servers": [ - { - "type": "sse", - "config": { - "url": "https://evalstate-flux1-schnell.hf.space/gradio_api/mcp/sse" - } - } - ] -} diff --git a/packages/tiny-agents/src/agents/julien-c/local-coder/PROMPT.md b/packages/tiny-agents/src/agents/julien-c/local-coder/PROMPT.md deleted file mode 100644 index e880614bf2..0000000000 --- a/packages/tiny-agents/src/agents/julien-c/local-coder/PROMPT.md +++ /dev/null @@ -1,5 +0,0 @@ -You are an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved, or if you need more info from the user to solve the problem. - -If you are not sure about anything pertaining to the user’s request, use your tools to read files and gather the relevant information: do NOT guess or make up an answer. - -You MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully. diff --git a/packages/tiny-agents/src/agents/julien-c/local-coder/agent.json b/packages/tiny-agents/src/agents/julien-c/local-coder/agent.json deleted file mode 100644 index 31a926839d..0000000000 --- a/packages/tiny-agents/src/agents/julien-c/local-coder/agent.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "model": "Qwen/Qwen2.5-72B-Instruct", - "provider": "nebius", - "servers": [ - { - "type": "stdio", - "config": { - "command": "npx", - "args": ["@playwright/mcp@latest"] - } - } - ] -} diff --git a/packages/tiny-agents/src/cli.ts b/packages/tiny-agents/src/cli.ts index 0722b394f0..2a670ee8d7 100644 --- a/packages/tiny-agents/src/cli.ts +++ b/packages/tiny-agents/src/cli.ts @@ -1,7 +1,5 @@ #!/usr/bin/env node -import { dirname, join } from "node:path"; import { parseArgs } from "node:util"; -import { lstat, readFile } from "node:fs/promises"; import { z } from "zod"; import { PROVIDERS_OR_POLICIES } from "@huggingface/inference"; import { Agent } from "@huggingface/mcp-client"; @@ -9,6 +7,7 @@ import { version as packageVersion } from "../package.json"; import { ServerConfigSchema } from "./lib/types"; import { debug, error } from "./lib/utils"; import { mainCliLoop } from "./lib/mainCliLoop"; +import { loadConfigFrom } from "./lib/loadConfigFrom"; const USAGE_HELP = ` Usage: @@ -30,55 +29,6 @@ function isValidCommand(command: string): command is (typeof CLI_COMMANDS)[numbe return (CLI_COMMANDS as unknown as string[]).includes(command); } -const FILENAME_CONFIG = "agent.json"; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const FILENAME_PROMPT = "PROMPT.md"; - -async function loadConfigFrom(loadFrom: string): Promise<{ configJson: string; prompt?: string }> { - try { - /// First try it as a local file path, then as a local directory, then we will try as a path inside the repo itself - return { - configJson: await readFile(loadFrom, { encoding: "utf8" }), - }; - } catch { - if ((await lstat(loadFrom)).isDirectory()) { - /// local directory - try { - let prompt: string | undefined; - try { - prompt = await readFile(join(loadFrom, FILENAME_PROMPT), { encoding: "utf8" }); - } catch { - debug(`PROMPT.md not found in ${loadFrom}, continuing without prompt template`); - } - return { - configJson: await readFile(join(loadFrom, FILENAME_CONFIG), { encoding: "utf8" }), - prompt, - }; - } catch { - error(`Config file not found in specified local directory.`); - process.exit(1); - } - } - const srcDir = dirname(__filename); - const configDir = join(srcDir, "agents", loadFrom); - try { - let prompt: string | undefined; - try { - prompt = await readFile(join(configDir, FILENAME_PROMPT), { encoding: "utf8" }); - } catch { - debug(`PROMPT.md not found in ${configDir}, continuing without prompt template`); - } - return { - configJson: await readFile(join(configDir, FILENAME_CONFIG), { encoding: "utf8" }), - prompt, - }; - } catch { - error(`Config file not found in tiny-agents repo! Loading from the HF Hub is not implemented yet`); - process.exit(1); - } - } -} - async function main() { const { values: { help, version }, diff --git a/packages/tiny-agents/src/lib/loadConfigFrom.ts b/packages/tiny-agents/src/lib/loadConfigFrom.ts new file mode 100644 index 0000000000..7396f110df --- /dev/null +++ b/packages/tiny-agents/src/lib/loadConfigFrom.ts @@ -0,0 +1,105 @@ +#!/usr/bin/env node +import { join } from "node:path"; +import { lstat, readFile } from "node:fs/promises"; +import { downloadFileToCacheDir, type RepoDesignation } from "@huggingface/hub"; +import type { TinyAgentConfig } from "./types"; +import { debug, error } from "./utils"; + +const FILENAME_CONFIG = "agent.json"; +const FILENAME_PROMPT = "PROMPT.md"; + +const TINY_AGENTS_HUB_REPO: RepoDesignation = { + name: "tiny-agents/tiny-agents", + type: "dataset", +}; + +async function tryLoadFromFile(filePath: string): Promise { + try { + const configJson = await readFile(filePath, { encoding: "utf8" }); + return { configJson }; + } catch { + return undefined; + } +} + +async function tryLoadFromDirectory(dirPath: string): Promise { + const stats = await lstat(dirPath).catch(() => undefined); + if (!stats?.isDirectory()) { + return undefined; + } + + let prompt: string | undefined; + try { + prompt = await readFile(join(dirPath, FILENAME_PROMPT), { encoding: "utf8" }); + } catch { + debug(`PROMPT.md not found in ${dirPath}, continuing without prompt template`); + } + + try { + return { + configJson: await readFile(join(dirPath, FILENAME_CONFIG), { encoding: "utf8" }), + prompt, + }; + } catch { + error(`Config file not found in specified local directory.`); + process.exit(1); + } +} + +async function tryLoadFromHub(agentId: string): Promise { + let configJson: string; + try { + const configPath = await downloadFileToCacheDir({ + repo: TINY_AGENTS_HUB_REPO, + path: `${agentId}/${FILENAME_CONFIG}`, + accessToken: process.env.HF_TOKEN, + }); + configJson = await readFile(configPath, { encoding: "utf8" }); + } catch { + return undefined; + } + + let prompt: string | undefined; + try { + const promptPath = await downloadFileToCacheDir({ + repo: TINY_AGENTS_HUB_REPO, + path: `${agentId}/${FILENAME_PROMPT}`, + accessToken: process.env.HF_TOKEN, + }); + prompt = await readFile(promptPath, { encoding: "utf8" }); + } catch { + debug( + `PROMPT.md not found in https://huggingface.co/datasets/tiny-agents/tiny-agents/tree/main/${agentId}, continuing without prompt template` + ); + } + + return { + configJson, + prompt, + }; +} + +export async function loadConfigFrom(loadFrom: string): Promise { + // First try as a local file + const fileConfig = await tryLoadFromFile(loadFrom); + if (fileConfig) { + return fileConfig; + } + + // Then try as a local directory + const dirConfig = await tryLoadFromDirectory(loadFrom); + if (dirConfig) { + return dirConfig; + } + + // Finally try loading from repo + const repoConfig = await tryLoadFromHub(loadFrom); + if (repoConfig) { + return repoConfig; + } + + error( + `Config file not found in tiny-agents! Please make sure it exists locally or in https://huggingface.co/datasets/tiny-agents/tiny-agents.` + ); + process.exit(1); +} diff --git a/packages/tiny-agents/src/lib/types.ts b/packages/tiny-agents/src/lib/types.ts index 6c3d7bce85..a2c6bf3d35 100644 --- a/packages/tiny-agents/src/lib/types.ts +++ b/packages/tiny-agents/src/lib/types.ts @@ -1,5 +1,10 @@ import { z } from "zod"; +export interface TinyAgentConfig { + configJson: string; + prompt?: string; +} + export const ServerConfigSchema = z.discriminatedUnion("type", [ z.object({ type: z.literal("stdio"),