@ridit/hackclub-ai-sdk
Advanced tools
| import type { | ||
| HackclubReplicateSTTModel, | ||
| HackclubReplicateTTSModel, | ||
| } from "./models"; | ||
| export const SSTInputMap: Record<HackclubReplicateSTTModel, string> = { | ||
| "vaibhavs10/incredibly-fast-whisper": "audio", | ||
| "nvidia/parakeet-rnnt-1.1b": "audio_file", | ||
| }; | ||
| export const TSSInputMap: Record<HackclubReplicateTTSModel, string> = { | ||
| "resemble-ai/chatterbox-pro": "prompt", | ||
| "minimax/speech-02-turbo": "text", | ||
| "zsxkib/dia": "text", | ||
| "lucataco/xtts-v2": "speaker", | ||
| "qwen/qwen3-tts": "text", | ||
| }; | ||
| export const SSTModelMap: Record< | ||
| HackclubReplicateSTTModel, | ||
| `${string}/${string}` | ||
| > = { | ||
| "vaibhavs10/incredibly-fast-whisper": | ||
| "vaibhavs10/incredibly-fast-whisper:3ab86df6c8f54c11309d4d1f930ac292bad43ace52d10c80d87eb258b3c9f79c", | ||
| "nvidia/parakeet-rnnt-1.1b": | ||
| "nvidia/parakeet-rnnt-1.1b:73ddbebaef172a47c8dfdd79381f110bfdc7691bcc7a4edde82f0a39e380ce50", | ||
| }; | ||
| export const TTSModelMap: Record< | ||
| HackclubReplicateTTSModel, | ||
| `${string}/${string}` | ||
| > = { | ||
| "zsxkib/dia": | ||
| "zsxkib/dia:2119e338ca5c0dacd3def83158d6c80d431f2ac1024146d8cca9220b74385599", | ||
| "resemble-ai/chatterbox-pro": "resemble-ai/chatterbox-pro", | ||
| "minimax/speech-02-turbo": "minimax/speech-02-turbo", | ||
| "lucataco/xtts-v2": | ||
| "lucataco/xtts-v2:684bc3855b37866c0c65add2ff39c78f3dea3f4ff103a436465326e0f438d55e", | ||
| "qwen/qwen3-tts": "qwen/qwen3-tts", | ||
| }; | ||
| export const BgRemovalModelMap: Record<string, `${string}/${string}`> = { | ||
| "lucataco/remove-bg": | ||
| "lucataco/remove-bg:95fcc2a26d3899cd6c2691c900465aaeff466285a65c14638cc5f36f34befaf1", | ||
| "851-labs/background-remover": | ||
| "851-labs/background-remover:a029dff38972b5fda4ec5d75d7d1cd25aeff621d2cf4946a41055d7db66b80bc", | ||
| }; | ||
| export const UpscaleModelMap: Record<string, `${string}/${string}`> = { | ||
| "google/upscaler": "google/upscaler", | ||
| "recraft-ai/recraft-crisp-upscale": "recraft-ai/recraft-crisp-upscale", | ||
| "fermatresearch/magic-image-refiner": | ||
| "fermatresearch/magic-image-refiner:507ddf6f977a7e30e46c0daefd30de7d563c72322f9e4cf7cbac52ef0f667b13", | ||
| }; |
| export const minimaxVoices = [ | ||
| // English | ||
| "English_Trustworth_Man", | ||
| "English_Aussie_Bloke", | ||
| "English_CalmWoman", | ||
| "English_UpsetGirl", | ||
| "English_Gentle-voiced_man", | ||
| "English_Whispering_girl", | ||
| "English_Diligent_Man", | ||
| "English_Graceful_Lady", | ||
| "English_ReservedYoungMan", | ||
| "English_PlayfulGirl", | ||
| "English_ManWithDeepVoice", | ||
| "English_MaturePartner", | ||
| "English_FriendlyPerson", | ||
| "English_MatureBoss", | ||
| "English_Debator", | ||
| "English_LovelyGirl", | ||
| "English_Steadymentor", | ||
| "English_Deep-VoicedGentleman", | ||
| "English_Wiselady", | ||
| "English_CaptivatingStoryteller", | ||
| "English_DecentYoungMan", | ||
| "English_SentimentalLady", | ||
| "English_ImposingManner", | ||
| "English_SadTeen", | ||
| "English_PassionateWarrior", | ||
| "English_WiseScholar", | ||
| "English_Soft-spokenGirl", | ||
| "English_SereneWoman", | ||
| "English_ConfidentWoman", | ||
| "English_PatientMan", | ||
| "English_Comedian", | ||
| "English_BossyLeader", | ||
| "English_Strong-WilledBoy", | ||
| "English_StressedLady", | ||
| "English_AssertiveQueen", | ||
| "English_AnimeCharacter", | ||
| "English_Jovialman", | ||
| "English_WhimsicalGirl", | ||
| "English_Kind-heartedGirl", | ||
| // Chinese | ||
| "Chinese (Mandarin)_Reliable_Executive", | ||
| "Chinese (Mandarin)_News_Anchor", | ||
| "Chinese (Mandarin)_Unrestrained_Young_Man", | ||
| "Chinese (Mandarin)_Mature_Woman", | ||
| "Arrogant_Miss", | ||
| "Robot_Armor", | ||
| "Chinese (Mandarin)_Kind-hearted_Antie", | ||
| "Chinese (Mandarin)_Refreshing_Young_Man", | ||
| "Chinese (Mandarin)_HK_Flight_Attendant", | ||
| "Chinese (Mandarin)_Humorous_Elder", | ||
| "Chinese (Mandarin)_Gentleman", | ||
| "Chinese (Mandarin)_Warm_Bestie", | ||
| "Chinese (Mandarin)_Stubborn_Friend", | ||
| "Chinese (Mandarin)_Sweet_Lady", | ||
| "Chinese (Mandarin)_Southern_Young_Man", | ||
| "Chinese (Mandarin)_Wise_Women", | ||
| "Chinese (Mandarin)_Gentle_Youth", | ||
| "Chinese (Mandarin)_Warm_Girl", | ||
| "Chinese (Mandarin)_Male_Announcer", | ||
| "Chinese (Mandarin)_Kind-hearted_Elder", | ||
| "Chinese (Mandarin)_Cute_Spirit", | ||
| "Chinese (Mandarin)_Radio_Host", | ||
| "Chinese (Mandarin)_Lyrical_Voice", | ||
| "Chinese (Mandarin)_Straightforward_Boy", | ||
| "Chinese (Mandarin)_Sincere_Adult", | ||
| "Chinese (Mandarin)_Gentle_Senior", | ||
| "Chinese (Mandarin)_Crisp_Girl", | ||
| "Chinese (Mandarin)_Pure-hearted_Boy", | ||
| "Chinese (Mandarin)_Soft_Girl", | ||
| "Chinese (Mandarin)_IntellectualGirl", | ||
| "Chinese (Mandarin)_Warm_HeartedGirl", | ||
| "Chinese (Mandarin)_Laid_BackGirl", | ||
| "Chinese (Mandarin)_ExplorativeGirl", | ||
| "Chinese (Mandarin)_Warm-HeartedAunt", | ||
| "Chinese (Mandarin)_BashfulGirl", | ||
| // Japanese | ||
| "Japanese_IntellectualSenior", | ||
| "Japanese_DecisivePrincess", | ||
| "Japanese_LoyalKnight", | ||
| "Japanese_DominantMan", | ||
| "Japanese_SeriousCommander", | ||
| "Japanese_ColdQueen", | ||
| "Japanese_DependableWoman", | ||
| "Japanese_GentleButler", | ||
| "Japanese_KindLady", | ||
| "Japanese_CalmLady", | ||
| "Japanese_OptimisticYouth", | ||
| "Japanese_GenerousIzakayaOwner", | ||
| "Japanese_SportyStudent", | ||
| "Japanese_InnocentBoy", | ||
| "Japanese_GracefulMaiden", | ||
| // Cantonese | ||
| "Cantonese_ProfessionalHost(F)", | ||
| "Cantonese_GentleLady", | ||
| "Cantonese_ProfessionalHost(M)", | ||
| "Cantonese_PlayfulMan", | ||
| "Cantonese_CuteGirl", | ||
| "Cantonese_KindWoman", | ||
| // Korean | ||
| "Korean_SweetGirl", | ||
| "Korean_CheerfulBoyfriend", | ||
| "Korean_EnchantingSister", | ||
| "Korean_ShyGirl", | ||
| "Korean_ReliableSister", | ||
| "Korean_StrictBoss", | ||
| "Korean_SassyGirl", | ||
| "Korean_ChildhoodFriendGirl", | ||
| "Korean_PlayboyCharmer", | ||
| "Korean_ElegantPrincess", | ||
| "Korean_BraveFemaleWarrior", | ||
| "Korean_BraveYouth", | ||
| "Korean_CalmLady", | ||
| "Korean_EnthusiasticTeen", | ||
| "Korean_SoothingLady", | ||
| "Korean_IntellectualSenior", | ||
| "Korean_LonelyWarrior", | ||
| "Korean_MatureLady", | ||
| "Korean_InnocentBoy", | ||
| "Korean_CharmingSister", | ||
| "Korean_AthleticStudent", | ||
| "Korean_BraveAdventurer", | ||
| "Korean_CalmGentleman", | ||
| "Korean_WiseElf", | ||
| "Korean_CheerfulCoolJunior", | ||
| "Korean_DecisiveQueen", | ||
| "Korean_ColdYoungMan", | ||
| "Korean_MysteriousGirl", | ||
| "Korean_QuirkyGirl", | ||
| "Korean_ConsiderateSenior", | ||
| "Korean_CheerfulLittleSister", | ||
| "Korean_DominantMan", | ||
| "Korean_AirheadedGirl", | ||
| "Korean_ReliableYouth", | ||
| "Korean_FriendlyBigSister", | ||
| "Korean_GentleBoss", | ||
| "Korean_ColdGirl", | ||
| "Korean_HaughtyLady", | ||
| "Korean_CharmingElderSister", | ||
| "Korean_IntellectualMan", | ||
| "Korean_CaringWoman", | ||
| "Korean_WiseTeacher", | ||
| "Korean_ConfidentBoss", | ||
| "Korean_AthleticGirl", | ||
| "Korean_PossessiveMan", | ||
| "Korean_GentleWoman", | ||
| "Korean_CockyGuy", | ||
| "Korean_ThoughtfulWoman", | ||
| "Korean_OptimisticYouth", | ||
| // Spanish | ||
| "Spanish_SereneWoman", | ||
| "Spanish_MaturePartner", | ||
| "Spanish_CaptivatingStoryteller", | ||
| "Spanish_Narrator", | ||
| "Spanish_WiseScholar", | ||
| "Spanish_Kind-heartedGirl", | ||
| "Spanish_DeterminedManager", | ||
| "Spanish_BossyLeader", | ||
| "Spanish_ReservedYoungMan", | ||
| "Spanish_ConfidentWoman", | ||
| "Spanish_ThoughtfulMan", | ||
| "Spanish_Strong-WilledBoy", | ||
| "Spanish_SophisticatedLady", | ||
| "Spanish_RationalMan", | ||
| "Spanish_AnimeCharacter", | ||
| "Spanish_Deep-tonedMan", | ||
| "Spanish_Fussyhostess", | ||
| "Spanish_SincereTeen", | ||
| "Spanish_FrankLady", | ||
| "Spanish_Comedian", | ||
| "Spanish_Debator", | ||
| "Spanish_ToughBoss", | ||
| "Spanish_Wiselady", | ||
| "Spanish_Steadymentor", | ||
| "Spanish_Jovialman", | ||
| "Spanish_SantaClaus", | ||
| "Spanish_Rudolph", | ||
| "Spanish_Intonategirl", | ||
| "Spanish_Arnold", | ||
| "Spanish_Ghost", | ||
| "Spanish_HumorousElder", | ||
| "Spanish_EnergeticBoy", | ||
| "Spanish_WhimsicalGirl", | ||
| "Spanish_StrictBoss", | ||
| "Spanish_ReliableMan", | ||
| "Spanish_SereneElder", | ||
| "Spanish_AngryMan", | ||
| "Spanish_AssertiveQueen", | ||
| "Spanish_CaringGirlfriend", | ||
| "Spanish_PowerfulSoldier", | ||
| "Spanish_PassionateWarrior", | ||
| "Spanish_ChattyGirl", | ||
| "Spanish_RomanticHusband", | ||
| "Spanish_CompellingGirl", | ||
| "Spanish_PowerfulVeteran", | ||
| "Spanish_SensibleManager", | ||
| "Spanish_ThoughtfulLady", | ||
| // French | ||
| "French_Male_Speech_New", | ||
| "French_Female_News Anchor", | ||
| "French_CasualMan", | ||
| "French_MovieLeadFemale", | ||
| "French_FemaleAnchor", | ||
| "French_MaleNarrator", | ||
| // Indonesian | ||
| "Indonesian_SweetGirl", | ||
| "Indonesian_ReservedYoungMan", | ||
| "Indonesian_CharmingGirl", | ||
| "Indonesian_CalmWoman", | ||
| "Indonesian_ConfidentWoman", | ||
| "Indonesian_CaringMan", | ||
| "Indonesian_BossyLeader", | ||
| "Indonesian_DeterminedBoy", | ||
| "Indonesian_GentleGirl", | ||
| // German | ||
| "German_FriendlyMan", | ||
| "German_SweetLady", | ||
| "German_PlayfulMan", | ||
| // Russian | ||
| "Russian_HandsomeChildhoodFriend", | ||
| "Russian_BrightHeroine", | ||
| "Russian_AmbitiousWoman", | ||
| "Russian_ReliableMan", | ||
| "Russian_CrazyQueen", | ||
| "Russian_PessimisticGirl", | ||
| "Russian_AttractiveGuy", | ||
| "Russian_Bad-temperedBoy", | ||
| // Italian | ||
| "Italian_BraveHeroine", | ||
| "Italian_Narrator", | ||
| "Italian_WanderingSorcerer", | ||
| "Italian_DiligentLeader", | ||
| // Others | ||
| "Dutch_kindhearted_girl", | ||
| "Dutch_bossy_leader", | ||
| "Vietnamese_kindhearted_girl", | ||
| "Arabic_CalmWoman", | ||
| "Arabic_FriendlyGuy", | ||
| "Turkish_CalmWoman", | ||
| "Turkish_Trustworthyman", | ||
| "Ukrainian_CalmWoman", | ||
| ] as const; | ||
| export const qwenVoices = ["Cherry", "Ethan", "Serena", "Dylan"] as const; | ||
| export type MinimaxVoice = (typeof minimaxVoices)[number]; | ||
| export type QwenVoice = (typeof qwenVoices)[number]; |
+1
-1
| { | ||
| "name": "@ridit/hackclub-ai-sdk", | ||
| "version": "0.1.1", | ||
| "version": "0.1.2", | ||
| "description": "One key. Every model. Hack Club powered.", | ||
@@ -5,0 +5,0 @@ "author": "Ridit Jangra <riditjangra09@gmail.com> (https://ridit.space)", |
+214
-73
| # @ridit/hackclub-ai-sdk | ||
| A lightweight TypeScript SDK for the [Hack Club AI](https://ai.hackclub.com) API — **free AI credits for hackers**. Get text and image generation with a clean, type-safe interface. | ||
| A lightweight TypeScript SDK for the [Hack Club AI](https://ai.hackclub.com) API — **free AI credits for hackers**. Text, image, speech, music, and more with a clean, type-safe interface. | ||
@@ -9,7 +9,9 @@ ## Why use this? | ||
| - **Zero config** — Just an API key, no complex setup | ||
| - **Type-safe** — Full TypeScript support with autocomplete for models | ||
| - **Simple API** — `generateText()` and `generateImage()` — that's it | ||
| - **Auto-save images** — Optional disk saving with custom filenames | ||
| - **Multiple models** — Access to Google, OpenAI, DeepSeek, Qwen, and more | ||
| - **Type-safe** — Full TypeScript support with autocomplete for models and voices | ||
| - **Batteries included** — Text, streaming, images, TTS, STT, background removal, upscaling, music | ||
| - **Auto-save** — Optional disk saving across all media methods | ||
| - **Multiple models** — Access to Google, OpenAI, DeepSeek, Qwen, Replicate, and more | ||
| --- | ||
| ## Quick Start | ||
@@ -39,41 +41,64 @@ | ||
| ```typescript | ||
| import { HackclubProvider } from "hackclub-ai-sdk"; | ||
| import { HackclubProvider } from "@ridit/hackclub-ai-sdk"; | ||
| // Initialize with your API key | ||
| const ai = new HackclubProvider(process.env.API_KEY ?? ""); | ||
| const ai = new HackclubProvider(process.env.HACKCLUB_API_KEY ?? ""); | ||
| // Generate text | ||
| const answer = await ai.generateText("Explain quantum computing in simple terms"); | ||
| const answer = await ai.generateText( | ||
| "Explain quantum computing in simple terms", | ||
| ); | ||
| console.log(answer); | ||
| // Generate and save an image | ||
| const { url } = await ai.generateImage("a cat astronaut floating in space", undefined, { | ||
| save: true, | ||
| filename: "space-cat.png" | ||
| }); | ||
| // Stream text | ||
| const stream = await ai.streamText("Tell me a story"); | ||
| for await (const chunk of stream) { | ||
| process.stdout.write(chunk); | ||
| } | ||
| ``` | ||
| --- | ||
| ## API Reference | ||
| ### `HackclubProvider(apiKey: string)` | ||
| ### `HackclubProvider(apiKey?: string)` | ||
| Main class for interacting with the Hack Club AI API. | ||
| Main class. If no key is passed, falls back to `HACKCLUB_API_KEY` env variable. | ||
| ```typescript | ||
| const ai = new HackclubProvider("your-api-key-here"); | ||
| // or | ||
| const ai = new HackclubProvider(); // uses process.env.HACKCLUB_API_KEY | ||
| ``` | ||
| ### `generateText(prompt: string, model?: HackclubModel): Promise<string>` | ||
| --- | ||
| ### Text | ||
| #### `generateText(prompt, model?, options?): Promise<string>` | ||
| Generates text from a prompt. | ||
| ```typescript | ||
| // Simple text generation | ||
| const text = await ai.generateText("Write a haiku about TypeScript"); | ||
| // With a specific model | ||
| // With model + options | ||
| const code = await ai.generateText( | ||
| "Write a React hook for debouncing", | ||
| "deepseek/deepseek-v3" // Great for coding tasks | ||
| "deepseek/deepseek-v3", | ||
| { | ||
| systemPrompt: "You are an expert TypeScript developer", | ||
| temperature: 0.7, | ||
| maxSteps: 3, | ||
| tools: { ... }, | ||
| } | ||
| ); | ||
| // Multi-turn conversation | ||
| const reply = await ai.generateText("", "google/gemini-2.5-flash", { | ||
| messages: [ | ||
| { role: "user", content: "What is TypeScript?" }, | ||
| { role: "assistant", content: "TypeScript is..." }, | ||
| { role: "user", content: "Give me an example" }, | ||
| ] | ||
| }); | ||
| ``` | ||
@@ -83,74 +108,182 @@ | ||
| ### `generateImage(prompt: string, model?: HackclubImageModel, options?): Promise<{ url: string; mimeType: string }>` | ||
| #### `streamText(prompt, model?, options?): Promise<AsyncIterable<string>>` | ||
| Same options as `generateText`, returns a token stream. | ||
| ```typescript | ||
| const stream = await ai.streamText("Tell me a story"); | ||
| for await (const chunk of stream) { | ||
| process.stdout.write(chunk); | ||
| } | ||
| ``` | ||
| --- | ||
| ### Images | ||
| #### `generateImage(prompt, model?, options?): Promise<{ url: string; mimeType: string }>` | ||
| Generates an image from a prompt. | ||
| ```typescript | ||
| // Basic image generation | ||
| const { url, mimeType } = await ai.generateImage("sunset over mountains"); | ||
| const { url } = await ai.generateImage("sunset over mountains"); | ||
| // Auto-save to disk | ||
| await ai.generateImage("cyberpunk city at night", undefined, { | ||
| save: true // → saves as generated-1234567890.png | ||
| }); | ||
| // Custom filename and aspect ratio | ||
| // With options | ||
| await ai.generateImage("cinematic movie poster", undefined, { | ||
| aspect_ratio: "21:9", | ||
| save: true, | ||
| filename: "movie-poster.png" | ||
| filename: "movie-poster.png", | ||
| }); | ||
| ``` | ||
| | Option | Type | Default | Description | | ||
| | -------------- | --------- | ----------------------------- | ------------------------------- | | ||
| | `aspect_ratio` | `string` | `"16:9"` | e.g. `"1:1"`, `"4:3"`, `"21:9"` | | ||
| | `save` | `boolean` | `false` | Auto-save to disk | | ||
| | `filename` | `string` | `generated-{timestamp}.{ext}` | Custom filename | | ||
| **Default model:** `google/gemini-2.5-flash-image` | ||
| #### Image Options | ||
| #### `removeBg(imageUrl, model?, options?): Promise<string>` | ||
| Remove the background from an image. | ||
| | Option | Type | Default | Description | | ||
| | -------------- | --------- | ----------------------------- | ------------------------------------------------------------------ | | ||
| | `aspect_ratio` | `string` | `"16:9"` | Aspect ratio of generated image (e.g., `"1:1"`, `"4:3"`, `"21:9"`) | | ||
| | `save` | `boolean` | `false` | Auto-save image to disk | | ||
| | `filename` | `string` | `generated-{timestamp}.{ext}` | Custom filename (only when `save: true`) | | ||
| ```typescript | ||
| const url = await ai.removeBg("https://example.com/photo.jpg"); | ||
| // Save result | ||
| const url = await ai.removeBg( | ||
| "https://example.com/photo.jpg", | ||
| "851-labs/background-remover", | ||
| { | ||
| save: true, | ||
| filename: "no-bg.png", | ||
| }, | ||
| ); | ||
| ``` | ||
| ## Available Models | ||
| **Available models:** `lucataco/remove-bg` (default), `851-labs/background-remover` | ||
| ### Text Models (`HackclubModel`) | ||
| #### `upscale(imageUrl, model?, options?): Promise<string>` | ||
| Upscale or refine an image. | ||
| | Model | Best For | Notes | | ||
| | ------------------------- | --------------------- | ----------------------------- | | ||
| | `google/gemini-2.5-flash` | General use | **Default**, fast and capable | | ||
| | `deepseek/deepseek-v3` | Code, reasoning | Great for programming tasks | | ||
| | `openai/gpt-4o` | Complex reasoning | OpenAI's flagship model | | ||
| | `qwen/qwen3.5-397b-a17b` | Chinese, multilingual | Strong for non-English | | ||
| | `moonshotai/kimi-k2.5` | Long context | Up to 128K tokens | | ||
| ```typescript | ||
| // 4x upscale | ||
| const url = await ai.upscale( | ||
| "https://example.com/image.png", | ||
| "google/upscaler", | ||
| { | ||
| upscale_factor: "x4", | ||
| save: true, | ||
| }, | ||
| ); | ||
| // AI refinement with prompt | ||
| const url = await ai.upscale( | ||
| "https://example.com/image.png", | ||
| "fermatresearch/magic-image-refiner", | ||
| { | ||
| prompt: "UHD 4k, highly detailed", | ||
| save: true, | ||
| }, | ||
| ); | ||
| ``` | ||
| *Full list in `[src/utils/models.ts](src/utils/models.ts)`* | ||
| | Option | Type | Default | Description | | ||
| | ---------------- | -------------- | -------------------------- | --------------------------------------------- | | ||
| | `upscale_factor` | `"x2" \| "x4"` | `"x2"` | Only for `google/upscaler` | | ||
| | `prompt` | `string` | — | Only for `fermatresearch/magic-image-refiner` | | ||
| | `save` | `boolean` | `false` | Auto-save to disk | | ||
| | `filename` | `string` | `upscaled-{timestamp}.png` | Custom filename | | ||
| ### Image Models (`HackclubImageModel`) | ||
| **Available models:** `google/upscaler` (default), `recraft-ai/recraft-crisp-upscale`, `fermatresearch/magic-image-refiner` | ||
| --- | ||
| | Model | Notes | | ||
| | --------------------------------------- | ------------------------- | | ||
| | `google/gemini-2.5-flash-image` | **Default**, high quality | | ||
| | `google/gemini-3.1-flash-image-preview` | Latest Google model | | ||
| ### Audio | ||
| #### `speak(text, model?, options?): Promise<string>` | ||
| ## Advanced Examples | ||
| Convert text to speech. Returns a URL to the generated audio file. | ||
| ### Streaming Responses (Coming Soon) | ||
| ```typescript | ||
| const url = await ai.speak("Hello from Hack Club!"); | ||
| // With voice + save | ||
| const url = await ai.speak("Hello!", "minimax/speech-02-turbo", { | ||
| voice: "English_CalmWoman", | ||
| save: true, | ||
| filename: "hello.mp3", | ||
| }); | ||
| ``` | ||
| **Available models:** `minimax/speech-02-turbo` (default), `resemble-ai/chatterbox-pro`, `zsxkib/dia`, `lucataco/xtts-v2`, `qwen/qwen3-tts` | ||
| **Available voices (minimax):** 300+ voices across English, Chinese, Japanese, Korean, Spanish, French, German, and more. Full list in `src/utils/voices.ts`. | ||
| #### `transcribe(audioFile, model?): Promise<string>` | ||
| Transcribe audio to text. Accepts a public URL or base64 data URI. | ||
| ```typescript | ||
| // Future feature - stream tokens as they arrive | ||
| for await (const token of ai.streamText("Tell me a story")) { | ||
| process.stdout.write(token); | ||
| } | ||
| // From URL | ||
| const text = await ai.transcribe("https://example.com/audio.mp3"); | ||
| // From local file | ||
| import { readFileSync } from "fs"; | ||
| const base64 = readFileSync("audio.mp3").toString("base64"); | ||
| const text = await ai.transcribe(`data:audio/mp3;base64,${base64}`); | ||
| ``` | ||
| ### Error Handling | ||
| **Available models:** `vaibhavs10/incredibly-fast-whisper` (default), `nvidia/parakeet-rnnt-1.1b` | ||
| --- | ||
| ### Music | ||
| #### `generateMusic(prompt, options?): Promise<string>` | ||
| Generate 48kHz stereo music using Google Lyria 2. | ||
| ```typescript | ||
| const url = await ai.generateMusic( | ||
| "Chill lofi beats with piano and rain sounds", | ||
| ); | ||
| // Save to disk | ||
| const url = await ai.generateMusic("Epic orchestral battle music", { | ||
| save: true, | ||
| filename: "battle.wav", | ||
| }); | ||
| ``` | ||
| --- | ||
| ## Available Models | ||
| ### Text Models (`HackclubModel`) | ||
| | Model | Best For | | ||
| | ------------------------- | --------------------- | | ||
| | `google/gemini-2.5-flash` | General use (default) | | ||
| | `deepseek/deepseek-v3.2` | Code, reasoning | | ||
| | `openai/gpt-5-mini` | Fast, capable | | ||
| | `moonshotai/kimi-k2.5` | Long context | | ||
| | `qwen/qwen3-235b-a22b` | Multilingual | | ||
| _Full list in `src/utils/models.ts`_ | ||
| ### Image Models (`HackclubImageModel`) | ||
| | Model | Notes | | ||
| | --------------------------------------- | ------- | | ||
| | `google/gemini-2.5-flash-image` | Default | | ||
| | `google/gemini-3.1-flash-image-preview` | Latest | | ||
| --- | ||
| ## Error Handling | ||
| ```typescript | ||
| try { | ||
@@ -160,23 +293,23 @@ const result = await ai.generateText("Generate some text"); | ||
| console.error("API error:", error); | ||
| // Handle rate limits, invalid API key, etc. | ||
| // Handle rate limits, invalid API key, network errors, etc. | ||
| } | ||
| ``` | ||
| > Replicate support is also coming soon! | ||
| --- | ||
| ### Environment Setup | ||
| ## Environment Setup | ||
| ```bash | ||
| # .env file | ||
| API_KEY=sk-hc-v1-your-key-here | ||
| # .env | ||
| HACKCLUB_API_KEY=sk-hc-v1-your-key-here | ||
| ``` | ||
| ```typescript | ||
| // In your code | ||
| import { config } from "dotenv"; | ||
| config(); | ||
| import { HackclubProvider } from "@ridit/hackclub-ai-sdk"; | ||
| const ai = new HackclubProvider(process.env.API_KEY!); | ||
| const ai = new HackclubProvider(); // auto-reads HACKCLUB_API_KEY | ||
| ``` | ||
| --- | ||
| ## Project Structure | ||
@@ -186,11 +319,15 @@ | ||
| src/ | ||
| ├── index.ts # Main exports | ||
| ├── provider.ts # HackclubProvider class | ||
| ├── index.ts # Main exports | ||
| ├── provider.ts # HackclubProvider class | ||
| ├── utils/ | ||
| │ ├── models.ts # Model definitions | ||
| │ └── url.ts # API endpoints | ||
| │ ├── models.ts # Model definitions | ||
| │ ├── voices.ts # Voice definitions | ||
| │ ├── replicate.ts # Replicate model/input maps | ||
| │ └── url.ts # API endpoints | ||
| └── types/ | ||
| └── types.ts # Type definitions | ||
| └── types.ts # Type definitions | ||
| ``` | ||
| --- | ||
| ## Development | ||
@@ -211,2 +348,4 @@ | ||
| --- | ||
| ## Contributing | ||
@@ -222,2 +361,4 @@ | ||
| --- | ||
| ## License | ||
@@ -229,2 +370,2 @@ | ||
| Built with ❤️ by [Ridit](https://github.com/ridit-jangra) for the Hack Club community. | ||
| Built with ❤️ by [Ridit](https://github.com/ridit-jangra) for the Hack Club community. |
+247
-26
| import { createOpenRouter } from "@openrouter/ai-sdk-provider"; | ||
| import { generateText as generateTextAISDK } from "ai"; | ||
| import type { HackclubImageModel, HackclubModel } from "./utils/models"; | ||
| import { | ||
| generateText as generateTextAISDK, | ||
| stepCountIs, | ||
| streamText as streamTextAISDK, | ||
| } from "ai"; | ||
| import type { | ||
| HackclubImageModel, | ||
| HackclubModel, | ||
| HackclubReplicateImageUtilModel, | ||
| HackclubReplicateSTTModel, | ||
| HackclubReplicateTTSModel, | ||
| HackclubReplicateUpscaleModel, | ||
| } from "./utils/models"; | ||
| import { BASE_URL } from "./utils/url"; | ||
| import Replicate from "replicate"; | ||
| import type { TextOptionProps, TTSOptions } from "./types/types"; | ||
| import { | ||
| BgRemovalModelMap, | ||
| SSTInputMap, | ||
| SSTModelMap, | ||
| TSSInputMap, | ||
| TTSModelMap, | ||
| UpscaleModelMap, | ||
| } from "./utils/replicate"; | ||
| /** | ||
| * Main provider for the Hackclub AI SDK. | ||
| */ | ||
| export class HackclubProvider { | ||
| private replicateProvider; | ||
| constructor(private apiKey: string) { | ||
| constructor(private apiKey: string = process.env.HACKCLUB_API_KEY ?? "") { | ||
| this.replicateProvider = new Replicate({ | ||
| auth: this.apiKey, | ||
| baseUrl: "https://ai.hackclub.com/proxy/v1/replicate", | ||
@@ -19,2 +37,17 @@ }); | ||
| private createProvider() { | ||
| return createOpenRouter({ | ||
| apiKey: this.apiKey, | ||
| baseURL: BASE_URL, | ||
| }); | ||
| } | ||
| private async saveFile(url: string, filename: string): Promise<void> { | ||
| const { writeFileSync } = await import("fs"); | ||
| const response = await fetch(url); | ||
| const buffer = Buffer.from(await response.arrayBuffer()); | ||
| writeFileSync(filename, buffer); | ||
| console.log(`Saved to ${filename}`); | ||
| } | ||
| /** | ||
@@ -24,2 +57,3 @@ * Generate text using a specified model. | ||
| * @param model - The model to use (auto-completions available) | ||
| * @param options - Generation options | ||
| * @returns Promise<string> - The generated text | ||
@@ -30,10 +64,21 @@ */ | ||
| model: HackclubModel = "google/gemini-2.5-flash", | ||
| options: TextOptionProps = {}, | ||
| ): Promise<string> { | ||
| const provider = createOpenRouter({ | ||
| apiKey: this.apiKey, | ||
| baseURL: BASE_URL, | ||
| }); | ||
| const { text } = await generateTextAISDK({ | ||
| model: provider(model), | ||
| prompt, | ||
| model: this.createProvider()(model), | ||
| ...(options.messages ? { messages: options.messages } : { prompt }), | ||
| tools: options.tools, | ||
| system: options.systemPrompt, | ||
| temperature: options.temperature, | ||
| maxOutputTokens: options.maxOutputTokens, | ||
| maxRetries: options.maxRetries, | ||
| headers: options.headers, | ||
| toolChoice: options.toolChoice, | ||
| onFinish: options.onFinish, | ||
| onStepFinish: options.onStepFinish, | ||
| output: options.output, | ||
| timeout: options.timeout, | ||
| experimental_onToolCallStart: options.experimental_onToolCallStart, | ||
| experimental_onToolCallFinish: options.experimental_onToolCallFinish, | ||
| ...(options.maxSteps ? { stopWhen: stepCountIs(options.maxSteps) } : {}), | ||
| }); | ||
@@ -45,2 +90,41 @@ | ||
| /** | ||
| * Stream text using a specified model. | ||
| * @param prompt - The text prompt to send to the model | ||
| * @param model - The model to use (auto-completions available) | ||
| * @param options - Generation options | ||
| * @returns Promise<AsyncIterable<string>> - A stream of text chunks | ||
| * @example | ||
| * const stream = await ai.streamText("Tell me a story"); | ||
| * for await (const chunk of stream) { | ||
| * process.stdout.write(chunk); | ||
| * } | ||
| */ | ||
| async streamText( | ||
| prompt: string, | ||
| model: HackclubModel = "google/gemini-2.5-flash", | ||
| options: TextOptionProps = {}, | ||
| ) { | ||
| const result = await streamTextAISDK({ | ||
| model: this.createProvider()(model), | ||
| ...(options.messages ? { messages: options.messages } : { prompt }), | ||
| tools: options.tools, | ||
| system: options.systemPrompt, | ||
| temperature: options.temperature, | ||
| maxOutputTokens: options.maxOutputTokens, | ||
| maxRetries: options.maxRetries, | ||
| headers: options.headers, | ||
| toolChoice: options.toolChoice, | ||
| onFinish: options.onFinish, | ||
| onStepFinish: options.onStepFinish, | ||
| output: options.output, | ||
| timeout: options.timeout, | ||
| experimental_onToolCallStart: options.experimental_onToolCallStart, | ||
| experimental_onToolCallFinish: options.experimental_onToolCallFinish, | ||
| ...(options.maxSteps ? { stopWhen: stepCountIs(options.maxSteps) } : {}), | ||
| }); | ||
| return result.textStream; | ||
| } | ||
| /** | ||
| * Generate an image using a specified model. | ||
@@ -52,3 +136,3 @@ * @param prompt - The text prompt to send to the model | ||
| * @param options.save - Whether to automatically save the image to disk | ||
| * @param options.filename - Custom filename for the saved image (defaults to "generated-{timestamp}.{ext}") | ||
| * @param options.filename - Custom filename for the saved image | ||
| * @returns Promise<{ url: string; mimeType: string }> - The generated image data URL and mime type | ||
@@ -59,7 +143,3 @@ */ | ||
| model: HackclubImageModel = "google/gemini-2.5-flash-image", | ||
| { | ||
| aspect_ratio = "16:9", | ||
| save = false, | ||
| filename, | ||
| }: { aspect_ratio?: string; save?: boolean; filename?: string } = {}, | ||
| options: { aspect_ratio?: string; save?: boolean; filename?: string } = {}, | ||
| ) { | ||
@@ -75,8 +155,6 @@ const response = await fetch( | ||
| body: JSON.stringify({ | ||
| model: model, | ||
| model, | ||
| messages: [{ role: "user", content: prompt }], | ||
| modalities: ["image", "text"], | ||
| image_config: { | ||
| aspect_ratio, | ||
| }, | ||
| image_config: { aspect_ratio: options.aspect_ratio ?? "16:9" }, | ||
| }), | ||
@@ -86,4 +164,3 @@ }, | ||
| const data = (await response.json()) as unknown as any; | ||
| const data = (await response.json()) as any; | ||
| const imageData = data.choices[0].message.images[0].image_url; | ||
@@ -93,6 +170,6 @@ const url = imageData.url; | ||
| if (save) { | ||
| if (options.save) { | ||
| const { writeFileSync } = await import("fs"); | ||
| const ext = mimeType.split("/")[1]; | ||
| const finalName = filename ?? `generated-${Date.now()}.${ext}`; | ||
| const finalName = options.filename ?? `generated-${Date.now()}.${ext}`; | ||
| const base64Data = url.replace(/^data:image\/\w+;base64,/, ""); | ||
@@ -105,2 +182,146 @@ writeFileSync(finalName, Buffer.from(base64Data, "base64")); | ||
| } | ||
| /** | ||
| * Transcribe audio to text using a specified model. | ||
| * @param audioFile - URL or base64 data URI of the audio file | ||
| * @param model - The STT model to use (auto-completions available) | ||
| * @returns Promise<string> - The transcribed text | ||
| * @example | ||
| * const text = await ai.transcribe("https://example.com/audio.mp3"); | ||
| */ | ||
| async transcribe( | ||
| audioFile: string, | ||
| model: HackclubReplicateSTTModel = "vaibhavs10/incredibly-fast-whisper", | ||
| ): Promise<string> { | ||
| const output = await this.replicateProvider.run(SSTModelMap[model], { | ||
| input: { [SSTInputMap[model]]: audioFile, batch_size: 64 }, | ||
| }); | ||
| return (output as any).text; | ||
| } | ||
| /** | ||
| * Convert text to speech using a specified model. | ||
| * @param text - The text to convert to speech | ||
| * @param model - The TTS model to use (auto-completions available) | ||
| * @param options - Generation options including voice and save settings | ||
| * @returns Promise<string> - URL to the generated audio file | ||
| * @example | ||
| * const url = await ai.speak("Hello from Hack Club!", "minimax/speech-02-turbo", { | ||
| * voice: "English_CalmWoman", | ||
| * save: true, | ||
| * }); | ||
| */ | ||
| async speak<T extends HackclubReplicateTTSModel>( | ||
| text: string, | ||
| model: T = "minimax/speech-02-turbo" as T, | ||
| options: TTSOptions<T> = {}, | ||
| ): Promise<string> { | ||
| const output = await this.replicateProvider.run(TTSModelMap[model], { | ||
| input: { | ||
| [TSSInputMap[model]]: text, | ||
| ...(options.voice ? { voice_id: options.voice } : {}), | ||
| }, | ||
| }); | ||
| const url = (output as any).url() as string; | ||
| if (options.save) { | ||
| await this.saveFile(url, options.filename ?? `tts-${Date.now()}.mp3`); | ||
| } | ||
| return url; | ||
| } | ||
| /** | ||
| * Remove the background from an image. | ||
| * @param imageUrl - URL of the image to process | ||
| * @param model - The background removal model to use | ||
| * @param options - Options for saving the output | ||
| * @returns Promise<string> - URL of the processed image | ||
| * @example | ||
| * const url = await ai.removeBg("https://example.com/photo.jpg"); | ||
| */ | ||
| async removeBg( | ||
| imageUrl: string, | ||
| model: HackclubReplicateImageUtilModel = "lucataco/remove-bg", | ||
| options: { save?: boolean; filename?: string } = {}, | ||
| ): Promise<string> { | ||
| const output = await this.replicateProvider.run(BgRemovalModelMap[model]!, { | ||
| input: { image: imageUrl }, | ||
| }); | ||
| const url = (output as any).url() as string; | ||
| if (options.save) { | ||
| await this.saveFile( | ||
| url, | ||
| options.filename ?? `bg-removed-${Date.now()}.png`, | ||
| ); | ||
| } | ||
| return url; | ||
| } | ||
| /** | ||
| * Upscale or refine an image using a specified model. | ||
| * @param imageUrl - URL of the image to upscale | ||
| * @param model - The upscale model to use | ||
| * @param options - Model-specific options including scale factor and prompt | ||
| * @returns Promise<string> - URL of the upscaled image | ||
| * @example | ||
| * const url = await ai.upscale("https://example.com/image.png", "google/upscaler", { | ||
| * upscale_factor: "x4" | ||
| * }); | ||
| */ | ||
| async upscale( | ||
| imageUrl: string, | ||
| model: HackclubReplicateUpscaleModel = "google/upscaler", | ||
| options: { | ||
| upscale_factor?: "x2" | "x4"; | ||
| prompt?: string; | ||
| save?: boolean; | ||
| filename?: string; | ||
| } = {}, | ||
| ): Promise<string> { | ||
| const input: Record<string, any> = { image: imageUrl }; | ||
| if (model === "google/upscaler") | ||
| input.upscale_factor = options.upscale_factor ?? "x2"; | ||
| if (model === "fermatresearch/magic-image-refiner" && options.prompt) | ||
| input.prompt = options.prompt; | ||
| const output = await this.replicateProvider.run(UpscaleModelMap[model]!, { | ||
| input, | ||
| }); | ||
| const url = Array.isArray(output) | ||
| ? ((output[0] as any).url() as string) | ||
| : ((output as any).url() as string); | ||
| if (options.save) { | ||
| await this.saveFile( | ||
| url, | ||
| options.filename ?? `upscaled-${Date.now()}.png`, | ||
| ); | ||
| } | ||
| return url; | ||
| } | ||
| /** | ||
| * Generate music from a text prompt using Google Lyria 2. | ||
| * @param prompt - Description of the music to generate | ||
| * @param options - Generation options | ||
| * @returns Promise<string> - URL of the generated audio file | ||
| * @example | ||
| * const url = await ai.generateMusic("Chill lofi beats with piano and rain sounds"); | ||
| */ | ||
| async generateMusic( | ||
| prompt: string, | ||
| options: { save?: boolean; filename?: string } = {}, | ||
| ): Promise<string> { | ||
| const output = await this.replicateProvider.run("google/lyria-2", { | ||
| input: { prompt }, | ||
| }); | ||
| const url = (output as any).url() as string; | ||
| if (options.save) { | ||
| await this.saveFile(url, options.filename ?? `music-${Date.now()}.wav`); | ||
| } | ||
| return url; | ||
| } | ||
| } |
+4
-7
| import { HackclubProvider } from "./provider"; | ||
| const asd = new HackclubProvider(process.env.API_KEY ?? ""); | ||
| const ai = new HackclubProvider(process.env.API_KEY ?? ""); | ||
| await asd.generateImage("sunset", undefined, { save: true }); | ||
| // → generated-1234567890.png | ||
| // custom name (ext auto-handled by the model's mime type) | ||
| await asd.generateImage("sunset", undefined, { | ||
| const url = await ai.speak("Hello from Hack Club!", "minimax/speech-02-turbo", { | ||
| save: true, | ||
| filename: "my-sunset.png", | ||
| voice: "English_AnimeCharacter", | ||
| }); | ||
| console.log(url); |
+79
-2
@@ -73,6 +73,73 @@ /** | ||
| export const hackclub_replicate_tts_models = [ | ||
| "minimax/speech-02-turbo", | ||
| "resemble-ai/chatterbox-pro", | ||
| "zsxkib/dia", | ||
| "lucataco/xtts-v2", | ||
| "qwen/qwen3-tts", | ||
| ] as const; | ||
| export const hackclub_replicate_stt_models = [ | ||
| "vaibhavs10/incredibly-fast-whisper", | ||
| "nvidia/parakeet-rnnt-1.1b", | ||
| ] as const; | ||
| export const hackclub_replicate_ocr_models = [ | ||
| "cuuupid/glm-4v-9b", | ||
| "lucataco/deepseek-ocr", | ||
| "abiruyt/text-extract-ocr", | ||
| ] as const; | ||
| export const hackclub_replicate_upscale_models = [ | ||
| "fermatresearch/magic-image-refiner", | ||
| "recraft-ai/recraft-crisp-upscale", | ||
| "google/upscaler", | ||
| ] as const; | ||
| export const hackclub_replicate_image_util_models = [ | ||
| "lucataco/remove-bg", | ||
| "851-labs/background-remover", | ||
| "zsxkib/ic-light-background", | ||
| "arielreplicate/robust_video_matting", | ||
| "lucataco/rembg-video", | ||
| "falcons-ai/nsfw_image_detection", | ||
| ] as const; | ||
| export const hackclub_replicate_music_models = [ | ||
| "google/lyria-2", | ||
| "meta/musicgen", | ||
| "minimax/music-1.5", | ||
| ] as const; | ||
| export const hackclub_replicate_specialized_models = [ | ||
| "retro-diffusion/rd-plus", | ||
| "geopti/sam-audio-large", | ||
| ] as const; | ||
| export type HackclubReplicateTTSModel = | ||
| (typeof hackclub_replicate_tts_models)[number]; | ||
| export type HackclubReplicateSTTModel = | ||
| (typeof hackclub_replicate_stt_models)[number]; | ||
| export type HackclubReplicateOCRModel = | ||
| (typeof hackclub_replicate_ocr_models)[number]; | ||
| export type HackclubReplicateUpscaleModel = | ||
| (typeof hackclub_replicate_upscale_models)[number]; | ||
| export type HackclubReplicateImageUtilModel = | ||
| (typeof hackclub_replicate_image_util_models)[number]; | ||
| export type HackclubReplicateMusicModel = | ||
| (typeof hackclub_replicate_music_models)[number]; | ||
| export type HackclubReplicateSpecializedModel = | ||
| (typeof hackclub_replicate_specialized_models)[number]; | ||
| export type HackclubModel = (typeof hackclub_models)[number]; | ||
| export type HackclubImageModel = (typeof hackclub_image_models)[number]; | ||
| export type HackclubReplicateModel = | ||
| | HackclubReplicateTTSModel | ||
| | HackclubReplicateSTTModel | ||
| | HackclubReplicateOCRModel | ||
| | HackclubReplicateUpscaleModel | ||
| | HackclubReplicateImageUtilModel | ||
| | HackclubReplicateMusicModel | ||
| | HackclubReplicateSpecializedModel; | ||
| /** | ||
@@ -83,3 +150,13 @@ * Get all available models as an array of strings. | ||
| export function getAvailableModels(): string[] { | ||
| return [...hackclub_models, ...hackclub_image_models]; | ||
| return [ | ||
| ...hackclub_models, | ||
| ...hackclub_image_models, | ||
| ...hackclub_replicate_tts_models, | ||
| ...hackclub_replicate_stt_models, | ||
| ...hackclub_replicate_ocr_models, | ||
| ...hackclub_replicate_upscale_models, | ||
| ...hackclub_replicate_image_util_models, | ||
| ...hackclub_replicate_music_models, | ||
| ...hackclub_replicate_specialized_models, | ||
| ]; | ||
| } |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
40767
139.9%13
18.18%813
310.61%364
63.96%4
33.33%3
200%