{"id":28534760,"url":"https://github.com/maruel/genai","last_synced_at":"2026-03-02T22:17:33.818Z","repository":{"id":281021850,"uuid":"943947511","full_name":"maruel/genai","owner":"maruel","description":"The opinionated high performance professional-grade AI package for Go","archived":false,"fork":false,"pushed_at":"2026-02-15T00:08:59.000Z","size":37577,"stargazers_count":23,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-02-15T07:55:10.197Z","etag":null,"topics":["ai","correctness","go","llm","performance"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/maruel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-03-06T14:30:46.000Z","updated_at":"2026-02-15T00:09:03.000Z","dependencies_parsed_at":"2025-03-20T21:52:54.070Z","dependency_job_id":"4068158d-78dd-4c75-9336-f1c86e8b3eb2","html_url":"https://github.com/maruel/genai","commit_stats":null,"previous_names":["maruel/genai"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/maruel/genai","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maruel%2Fgenai","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maruel%2Fgenai/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maruel%2Fgenai/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maruel%2Fgenai/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maruel","download_url":"https://codeload.github.com/maruel/genai/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maruel%2Fgenai/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29601518,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-19T02:50:40.506Z","status":"ssl_error","status_checked_at":"2026-02-19T02:50:26.316Z","response_time":117,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","correctness","go","llm","performance"],"created_at":"2025-06-09T17:12:47.058Z","updated_at":"2026-03-02T22:17:33.811Z","avatar_url":"https://github.com/maruel.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# genai\n\nThe opinionated high performance professional-grade AI package for Go.\n\ngenai is _intentional_. Curious why it was created? See the release announcement at\n[maruel.ca/post/genai-v0.1.0](https://maruel.ca/post/genai-v0.1.0).\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/maruel/genai/.svg)](https://pkg.go.dev/github.com/maruel/genai/)\n[![codecov](https://codecov.io/gh/maruel/genai/graph/badge.svg?token=VLBH363B6N)](https://codecov.io/gh/maruel/genai)\n\n\n## Features\n\n- **Full functionality**: Full access to each backend-specific functionality.\n  Access the raw API if needed with full message schema as Go structs.\n- **Tool calling via reflection**: Tell the LLM to call a tool directly, described as a Go\n  struct. No need to manually fiddle with JSON.\n- **Native JSON struct serialization**: Pass a struct to tell the LLM what to\n  generate, decode the reply into your struct. No need to manually fiddle with\n  JSON. Supports required fields, enums, descriptions, etc. You can still fiddle if you want to. :)\n- **Streaming**: Streams completion reply as the output is being generated, including thinking and tool\n  calling, via [go 1.23 iterators](https://go.dev/blog/range-functions).\n- **Multi-modal**: Process images, PDFs and videos (!) as input or output.\n- **Web Search**: Search the web to answer your question and cite documents passed in.\n- **Smoke testing friendly**: record and play back API calls at HTTP level to save 💰 and keep tests fast and\n  reproducible, via the exposed HTTP transport. See [example](https://pkg.go.dev/github.com/maruel/genai/providers/anthropic#example-New-HTTP_record).\n- **Rate limits and usage**: Parse the provider-specific HTTP headers and JSON response to get the tokens usage\n  and remaining quota.\n- Provide access to HTTP headers to enable [beta features](https://pkg.go.dev/github.com/maruel/genai#example-package-GenSyncWithToolCallLoop_with_custom_HTTP_Header).\n\n\n## Design\n\n- **Safe and strict API implementation**. All you love from a statically typed\n  language. The library's smoke tests immediately fail on unknown RPC fields. Error code paths are properly\n  implemented.\n- **Stateless**. No global state, it is safe to use clients concurrently.\n- **Professional grade**. smoke tested on live services with recorded traces located in `testdata/`\n  directories, e.g.\n  [providers/anthropic/testdata/TestClient/Scoreboard/](https://github.com/maruel/genai/tree/main/providers/anthropic/testdata/TestClient/Scoreboard/).\n- **Trust, but verify**. It generates a [scoreboard](#scoreboard) based on actual behavior from each provider.\n- **Optimized for speed**. Minimize memory allocations, compress data at the\n  transport layer when possible. Groq, Mistral and OpenAI use brotli for HTTP compression instead of gzip,\n  and POST's body to Google are gzip compressed.\n- **Lean**: Few dependencies. No unnecessary abstraction layer.\n\n\n## Scoreboard\n\n| Provider                                   | 🌐   | Mode          | ➛In        | Out➛   | Tool   | JSON | Batch | File | Cite | Text | Probs | Limits | Usage | Finish |\n| ------------------------------------------ | ---- | ------------- | ---------- | ------ | ------ | ---- | ----- | ---- | ---- | ---- | ----- | ------ | ----- | ------ |\n| [alibaba](docs/alibaba.md)                 | 🇨🇳   | Sync, Stream🧠 | 💬📸       | 💬     | ✅🪨   | ☁️   | ❌    | ❌   | ❌   | 🌱📏🛑 | ✅    | ❌     | ✅    | ✅     |\n| [anthropic](docs/anthropic.md)             | 🇺🇸   | Sync, Stream🧠 | 💬📄📸     | 💬     | ✅🪨🕸️ | 📐    | ✅    | ❌   | ✅   | 🛑📏   | ❌    | ✅     | ✅    | ✅     |\n| [baseten](docs/baseten.md)                 | 🇺🇸   | Sync, Stream🧠 | 💬         | 💬     | ✅🪨   | ✅   | ❌    | ❌   | ❌   | 🌱📏🛑 | ❌    | ❌     | ✅    | ✅     |\n| [bfl](docs/bfl.md)                         | 🇩🇪   | Sync          | 💬         | 📸     | ❌     | ❌   | ✅    | ❌   | ❌   | 🌱   | ❌    | ✅     | ❌    | ❌     |\n| [cerebras](docs/cerebras.md)               | 🇺🇸   | Sync, Stream🧠 | 💬         | 💬     | ✅🪨   | ✅   | ❌    | ❌   | ❌   | 🌱📏🛑 | ✅    | ❌     | ✅    | ✅     |\n| [claudecode](docs/claudecode.md)           | 🇺🇸   | Sync, Stream  | 💬         | 💬     | ❌     | ❌   | ❌    | ❌   | ❌   | ❌   | ❌    | ❌     | ✅    | ✅     |\n| [cloudflare](docs/cloudflare.md)           | 🇺🇸   | Sync, Stream🧠 | 💬         | 💬     | 💨     | ✅   | ❌    | ❌   | ❌   | 🌱📏  | ❌    | ❌     | ✅    | 💨     |\n| [cohere](docs/cohere.md)                   | 🇨🇦   | Sync, Stream🧠 | 💬📸       | 💬     | ✅🪨   | ✅   | ❌    | ❌   | ✅   | 🌱📏🛑 | ✅    | ❌     | ✅    | ✅     |\n| [deepseek](docs/deepseek.md)               | 🇨🇳   | Sync, Stream🧠 | 💬         | 💬     | ✅🪨   | ☁️   | ❌    | ❌   | ❌   | 📏🛑   | ✅    | ❌     | ✅    | ✅     |\n| [gemini](docs/gemini.md)                   | 🇺🇸   | Sync, Stream🧠 | 🎤🎥💬📄📸 | 💬📸   | ✅🪨🕸️ | ✅   | ❌    | ✅   | ❌   | 🌱📏🛑 | ❌    | ❌     | ✅    | ✅     |\n| [groq](docs/groq.md)                       | 🇺🇸   | Sync, Stream🧠 | 💬📸       | 💬     | ✅🪨🕸️ | ☁️   | ❌    | ❌   | ❌   | 🌱📏🛑 | ❌    | ✅     | ✅    | ✅     |\n| [huggingface](docs/huggingface.md)         | 🇺🇸   | Sync, Stream🧠 | 💬         | 💬     | ❌     | ☁️   | ❌    | ❌   | ❌   | 🌱📏🛑 | ✅    | ✅     | ✅    | ✅     |\n| [llamacpp](docs/llamacpp.md)               | 🏠   | Sync, Stream🧠 | 💬📸       | 💬     | ✅🪨   | ✅   | ❌    | ❌   | ❌   | 🌱📏🛑 | ✅    | ❌     | ✅    | ✅     |\n| [mistral](docs/mistral.md)                 | 🇫🇷   | Sync, Stream  | 🎤💬📄📸   | 💬     | ✅🪨   | ✅   | ❌    | ❌   | ❌   | 🌱📏🛑 | ❌    | ✅     | ✅    | ✅     |\n| [ollama](docs/ollama.md)                   | 🏠   | Sync, Stream🧠 | 💬📸       | 💬     | 💨     | ✅   | ❌    | ❌   | ❌   | 🌱📏🛑 | ✅    | ❌     | ✅    | ✅     |\n| [openaichat](docs/openaichat.md)           | 🇺🇸   | Sync, Stream🧠 | 🎤💬📄📸   | 💬📸   | ✅🪨🕸️ | ✅   | ✅    | ✅   | ❌   | 🌱📏🛑 | ✅    | ✅     | ✅    | ✅     |\n| [openairesponses](docs/openairesponses.md) | 🇺🇸   | Sync, Stream🧠 | 💬📄📸     | 💬📸   | ✅🪨🕸️ | ✅   | ✅    | ❌   | ❌   | ❌   | ❌    | ✅     | ✅    | ✅     |\n| [openrouter](docs/openrouter.md)           | 🇺🇸   | Sync, Stream🧠 | 💬📸       | 💬     | ✅🪨   | ☁️   | ❌    | ❌   | ❌   | 🌱📏🛑 | ❌    | ❌     | ✅    | ✅     |\n| [perplexity](docs/perplexity.md)           | 🇺🇸   | Sync, Stream🧠 | 💬📸       | 💬     | 🕸️     | 📐    | ❌    | ❌   | ✅   | 📏    | ❌    | ❌     | ✅    | ✅     |\n| [pollinations](docs/pollinations.md)       | 🇩🇪   | Sync, Stream  | 💬📸       | 💬📸   | ✅🪨   | ☁️   | ❌    | ❌   | ❌   | 🌱   | ❌    | ❌     | ✅    | ✅     |\n| [togetherai](docs/togetherai.md)           | 🇺🇸   | Sync, Stream🧠 | 🎥💬📸     | 💬📸   | ✅🪨   | ✅   | ❌    | ❌   | ❌   | 🌱📏🛑 | ❌    | ✅     | ✅    | ✅     |\n| openaicompatible                           | N/A  | Sync, Stream  | 💬         | 💬     | ❌     | ❌   | ❌    | ❌   | ❌   | 📏🛑   | ❌    | ❌     | ✅    | ✅     |\n\u003cdetails\u003e\n\u003csummary\u003e‼️ Click here for the legend of columns and symbols\u003c/summary\u003e\n\n- 🏠: Runs locally.\n- Sync:   Runs synchronously, the reply is only returned once completely generated\n- Stream: Streams the reply as it is generated. Occasionally less features are supported in this mode\n- 🧠: Has chain-of-thought thinking process\n    - Both redacted (Anthropic, Gemini, OpenAI) and explicit (Deepseek R1, Qwen3, etc)\n    - Many models can be used in both mode. In this case they will have two rows, one with thinking and one\n      without. It is frequent that certain functionalities are limited in thinking mode, like tool calling.\n- ✅: Implemented and works great\n- ❌: Not supported by genai. The provider may support it, but genai does not (yet). Please send a PR to add\n  it!\n- 💬: Text\n- 📄: PDF: process a PDF as input, possibly with OCR\n- 📸: Image: process an image as input; most providers support PNG, JPG, WEBP and non-animated GIF, or generate images\n- 🎤: Audio: process an audio file (e.g. MP3, WAV, Flac, Opus) as input, or generate audio\n- 🎥: Video: process a video (e.g. MP4) as input, or generate a video (e.g. Veo 3)\n- 💨: Feature is flaky (Tool calling) or inconsistent (Usage is not always reported)\n- 🌐: Country where the company is located\n- Tool: Tool calling, using [genai.ToolDef](https://pkg.go.dev/github.com/maruel/genai#ToolDef); best is ✅🪨🕸️\n\t\t- 🪨: Tool calling can be forced; aka you can force the model to call a tool. This is great.\n\t\t- 🕸️: Web search\n- JSON: ability to output JSON in free form, or with a forced schema specified as a Go struct\n    - ✅: Supports both free form and with a schema\n    - ☁️ :Supports only free form\n\t\t- 📐: Supports only a schema\n- Batch: Process asynchronously batches during off peak hours at a discounts\n- Text: Text features\n    - '🌱': Seed option for deterministic output\n    - '📏': MaxTokens option to cap the amount of returned tokens\n    - '🛑': Stop sequence to stop generation when a token is generated\n- File: Upload and store large files via a separate API\n- Cite: Citation generation from a provided document, specially useful for RAG\n- Probs: Return logprobs to analyse each token probabilities\n- Limits: Returns the rate limits, including the remaining quota\n\u003c/details\u003e\n\n\n## Examples\n\nThe following examples intentionally use a variety of providers to show the extent at which you can pick and\nchose.\n\n### Text Basic ✅\n\n[examples/txt\\_to\\_txt\\_sync/main.go](examples/txt_to_txt_sync/main.go): This selects a good default model based\non Anthropic's currently published models, sends a prompt and prints the response as a string.\n💡 Set [`ANTHROPIC_API_KEY`](https://console.anthropic.com/settings/keys).\n\n```go\nfunc main() {\n\tctx := context.Background()\n\tc, err := anthropic.New(ctx, genai.ModelGood)\n\tmsgs := genai.Messages{\n\t\tgenai.NewTextMessage(\"Give me a life advice that sounds good but is a bad idea in practice. Answer succinctly.\"),\n\t}\n\tresult, err := c.GenSync(ctx, msgs)\n\tfmt.Println(result.String())\n}\n```\n\nThis may print:\n\n\u003e \"Follow your passion and the money will follow.\"\n\u003e\n\u003e This ignores market realities, financial responsibilities, and the fact that passion alone doesn't guarantee\n\u003e income or career viability.\n\n\n### Multiple Text Completions\n\n[examples/txt\\_to\\_txt\\_sync\\_multi/main.go](examples/txt_to_txt_sync_multi.go): This shows how to do multiple message\nround trips adding additional follow-up messages from users. Set [`OPENAI_API_KEY`](https://platform.openai.com/api-keys).\n\n```go\nfunc main() {\n\tctx := context.Background()\n\tc, err := anthropic.New(ctx, genai.ModelGood)\n\tmsgs := genai.Messages{\n\t\tgenai.NewTextMessage(\"Let's play a word association game. You pick a single word, then I pick the first word I think of, then you respond with a word, and so on.\")\n\t}\n\tresult, err := c.GenSync(ctx, msgs)\n    if err != nil {\n        panic(err)\n    }\n    // Show the message from ChatGPT\n\tfmt.Println(result.String())\n    // Save the message in the collection of messages to build up context\n    msgs = append(msgs, result.Message)\n    // Add another user message\n    msgs = append(msgs, genai.NewTextMessage(\"nightwish\"))\n    // Get another completion\n    result, err := c.GenSync(ctx, msgs)\n    // ...and so on.\n}\n```\n\n### Text Streaming 🏎\n\n[examples/txt\\_to\\_txt\\_stream/main.go](examples/txt_to_txt_stream/main.go): This is the same example as\nabove, with the output streamed as it replies. This leverages [go 1.23\niterators](https://go.dev/blog/range-functions). Notice how little difference there is between both.\n\n```go\nfunc main() {\n\tctx := context.Background()\n\tc, err := anthropic.New(ctx, genai.ModelGood)\n\tmsgs := genai.Messages{\n\t\tgenai.NewTextMessage(\"Give me a life advice that sounds good but is a bad idea in practice.\"),\n\t}\n\tfragments, finish := c.GenStream(ctx, msgs)\n\tfor f := range fragments {\n\t\tos.Stdout.WriteString(f.Text)\n\t}\n\t_, err = finish()\n}\n```\n\n\n### Text Thinking 🧠\n\n[examples/txt\\_to\\_txt\\_thinking/main.go](examples/txt_to_txt_thinking/main.go): genai supports for implicit\nreasoning (e.g. Anthropic) and explicit reasoning (e.g. Deepseek). The package adapters provide logic to\nautomatically handle explicit Chain-of-Thoughts models, generally using `\u003cthink\u003e` and `\u003c/think\u003e` tokens.\n💡 Set [`DEEPSEEK_API_KEY`](https://platform.deepseek.com/api_keys).\n\nSnippet:\n\n```go\n\tc, _ := deepseek.New(ctx, genai.ProviderOptionModel(\"deepseek-reasoner\"))\n\tmsgs := genai.Messages{\n\t\tgenai.NewTextMessage(\"Give me a life advice that sounds good but is a bad idea in practice.\"),\n\t}\n\tfragments, finish := c.GenStream(ctx, msgs)\n\tfor f := range fragments {\n\t\tif f.Reasoning != \"\" {\n\t\t\t// ...\n\t\t} else if f.Text != \"\" {\n\t\t\t// ...\n\t\t}\n\t}\n```\n\n\n### Text Citations ✍\n\n[examples/txt\\_to\\_txt\\_citations/main.go](examples/txt_to_txt_citations/main.go): Send entire documents and\nleverage providers which support automatic citations (Cohere, Anthropic) to leverage their functionality for a\nsupercharged RAG.\n💡 Set [`COHERE_API_KEY`](https://dashboard.cohere.com/api-keys).\n\nSnippet:\n\n```go\n\tconst context = `...` // Introduction of On the Origin of Species by Charles Darwin...\n\tmsgs := genai.Messages{{\n\t\tRequests: []genai.Request{\n\t\t\t{\n\t\t\t\tDoc: genai.Doc{\n\t\t\t\t\tFilename: \"On-the-Origin-of-Species-by-Charles-Darwin.txt\",\n\t\t\t\t\tSrc:      strings.NewReader(context),\n\t\t\t\t},\n\t\t\t},\n\t\t\t{Text: \"When did Darwin arrive home?\"},\n\t\t},\n\t}}\n\tres, _ := c.GenSync(ctx, msgs)\n\tfor _, r := range res.Replies {\n\t\tif !r.Citation.IsZero() {\n\t\t\tfmt.Printf(\"Citation:\\n\")\n\t\t\tfor _, src := range r.Citation.Sources {\n\t\t\t\tfmt.Printf(\"- %q\\n\", src.Snippet)\n\t\t\t}\n\t\t}\n\t}\n\tfmt.Printf(\"\\nAnswer: %s\\n\", res.String())\n```\n\nWhen asked _When did Darwin arrive home?_ with the introduction of _On the Origin of Species by Charles\nDarwin_ passed in as a document, this may print:\n\n\u003e Citation:\n\u003e - \"excerpt from Charles Darwin's work 'On the Origin of Species'\"\n\u003e - \"returned home in 1837.\"\n\u003e\n\u003e Answer: 1837 was when Darwin returned home and began to reflect on the facts he had gathered during his time on H.M.S. Beagle.\n\n\n### Text Websearch 🕸️\n\n[examples/txt\\_to\\_txt\\_websearch-sync/main.go](examples/txt_to_txt_websearch-sync/main.go): Searches the web\nto answer your question.\n💡 Set [`PERPLEXITY_API_KEY`](https://www.perplexity.ai/settings/api).\n\nSnippet:\n\n```go\n\tc, _ := perplexity.New(ctx, genai.ModelCheap)\n\tmsgs := genai.Messages{{\n\t\tRequests: []genai.Request{\n\t\t\t{Text: \"Who holds ultimate power of Canada? Answer succinctly.\"},\n\t\t},\n\t}}\n\n\t// perplexity has websearch enabled by default so this is a no-op.\n\t//  It is needed to enable websearch for anthropic, gemini and openai.\n\topts := genai.GenOptionWeb{Search: true, Fetch: true}\n\tres, _ := c.GenSync(ctx, msgs, \u0026opts)\n\tfor _, r := range res.Replies {\n\t\tif !r.Citation.IsZero() {\n\t\t\tfmt.Printf(\"Sources:\\n\")\n\t\t\tfor _, src := range r.Citation.Sources {\n\t\t\t\tswitch src.Type {\n\t\t\t\tcase genai.CitationWeb:\n\t\t\t\t\tfmt.Printf(\"- %s / %s\\n\", src.Title, src.URL)\n\t\t\t\tcase genai.CitationWebImage:\n\t\t\t\t\tfmt.Printf(\"- image: %s\\n\", src.URL)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tfmt.Printf(\"\\nAnswer: %s\\n\", res.String())\n```\n\nTry it locally:\n\n```bash\ngo run github.com/maruel/genai/examples/txt_to_txt_websearch-sync@latest\n```\n\nWhen asked _Who holds ultimate power of Canada?_, this may print:\n\n\u003e Sources:\n\u003e\n\u003e - Prime Minister of Canada / https://en.wikipedia.org/wiki/Prime_Minister_of_Canada\n\u003e\n\u003e - Canadian Parliamentary System - Our Procedure /\n\u003e https://www.ourcommons.ca/procedure/our-procedure/parliamentaryFramework/c_g_parliamentaryframework-e.html\n\u003e\n\u003e\n\u003e (...)\n\u003e\n\u003e Image: https://learn.parl.ca/understanding-comprendre/images/articles/monarch-and-governor-general/house-of-commons.jpg\n\u003e\n\u003e (...)\n\u003e\n\u003e Answer: The **ultimate power in Canada** constitutionally resides with the **monarch (King Charles III)** as\n\u003e the head of state, with executive authority formally vested in him. However, (...)\n\n\n### Text Websearch (streaming) 🔍️\n\n[examples/txt\\_to\\_txt\\_websearch-stream/main.go](examples/txt_to_txt_websearch-stream/main.go): Searches the web\nto answer your question and streams the output to the console.\n💡 Set [`PERPLEXITY_API_KEY`](https://www.perplexity.ai/settings/api).\n\n```bash\ngo run github.com/maruel/genai/examples/txt_to_txt_websearch-stream@latest\n```\n\nSame as above, but streaming.\n\n\n### Log probabilities\n\n[examples/txt\\_to\\_txt\\_logprobs/main.go](examples/txt_to_txt_logprobs/main.go): List the alternative tokens\nthat were considered during generation. This helps tune Temperature, TopP or TopK.\n\nTry it locally:\n\n```bash\ngo run github.com/maruel/genai/examples/txt_to_txt_logprobs@latest\n```\n\nWhen asked _Tell a joke_, this may print:\n\n```\nProvider huggingface\n  Reply:\n    Why don't scientists trust atoms?\n\n    Because they make up everything!\n  Logprobs:\n    *    -0.000082: \"Why\"\n         -9.625082: \"Here\"\n        -11.250082: \"What\"\n        -13.875082: \"A\"\n        -14.500082: \"How\"\n    *    -0.000003: \" don\"\n        -14.125003: \" do\"\n        -14.625003: \" did\"\n        -14.625003: \" dont\"\n        -14.875003: \" didn\"\n    *    -0.000001: \"'t\"\n        -14.000001: \"’t\"\n        -18.062500: \"'\"\n        -18.875000: \"'T\"\n        -19.812500: \"'s\"\n    *    -0.000002: \" scientists\"\n        -14.250002: \" Scientists\"\n        -14.250002: \" eggs\"\n        -15.125002: \" skeletons\"\n        -16.125002: \" programmers\"\n    *    -0.000000: \" trust\"\n        -16.250000: \" trusts\"\n        -16.250000: \" Trust\"\n        -17.250000: \" like\"\n        -18.000000: \" trusted\"\n    *    -0.000006: \" atoms\"\n        -13.250006: \"atoms\"\n        -13.500006: \" stairs\"\n        -14.625006: \" their\"\n        -15.000006: \" electrons\"\n    *    -0.000011: \"?\\n\\n\"\n        -12.125011: \"?\\n\"\n        -12.125011: \"?\"\n        -14.750011: \"？\\n\\n\"\n        -16.500011: \" anymore\"\n(...)\n```\n\n\n### Text Tools 🧰\n\n[examples/txt\\_to\\_txt\\_tool-sync/main.go](examples/txt_to_txt_tool-sync/main.go): A LLM can both retrieve\ninformation and act on its environment through tool calling. This unblocks a whole realm of possibilities. Our\ndesign enables dense strongly typed code that favorably compares to python.\n💡 Set [`CEREBRAS_API_KEY`](https://cloud.cerebras.ai/platform/).\n\nSnippet:\n\n```go\n\ttype numbers struct {\n\t\tA int `json:\"a\"`\n\t\tB int `json:\"b\"`\n\t}\n\tmsgs := genai.Messages{\n\t\tgenai.NewTextMessage(\"What is 3214 + 5632? Call the tool \\\"add\\\" to tell me the answer. Do not explain. Be terse. Include only the answer.\"),\n\t}\n\topts := genai.GenOptionTools{\n\t\tTools: []genai.ToolDef{\n\t\t\t{\n\t\t\t\tName:        \"add\",\n\t\t\t\tDescription: \"Add two numbers together and provides the result\",\n\t\t\t\tCallback: func(ctx context.Context, input *numbers) (string, error) {\n\t\t\t\t\treturn fmt.Sprintf(\"%d\", input.A+input.B), nil\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t// Force the LLM to do a tool call.\n\t\tForce: genai.ToolCallRequired,\n\t}\n\n\t// Run the loop.\n\tres, _, _ := adapters.GenSyncWithToolCallLoop(ctx, c, msgs, \u0026opts)\n\t// Print the answer which is the last message generated.\n\tfmt.Println(res[len(res)-1].String())\n```\n\nWhen asked _What is 3214 + 5632?_, this may print:\n\n\u003e 8846\n\n\n### Text Tools (streaming) 🐝\n\n[examples/txt\\_to\\_txt\\_tool-stream/main.go](examples/txt_to_txt_tool-stream/main.go): Leverage a thinking\nmodel to see the thinking process while trying to use tool calls to answer the user's question. This enables\nkeeping the user updated to see the progress.\n💡 Set [`GROQ_API_KEY`](https://console.groq.com/keys).\n\nSnippet:\n\n```go\n\tfragments, finish := adapters.GenStreamWithToolCallLoop(ctx, p, msgs, \u0026opts)\n\tfor f := range fragments {\n\t\tif f.Reasoning != \"\" {\n\t\t\t// ...\n\t\t} else if f.Text != \"\" {\n\t\t\t// ...\n\t\t} else if !f.ToolCall.IsZero() {\n\t\t\t// ...\n\t\t}\n\t}\n```\n``\n\nWhen asked _What is 3214 + 5632?_, this may print:\n\n\u003e \\# Reasoning\n\u003e\n\u003e User wants result of 3214+5632 using tool \"add\". Must be terse, only answer, no explanation. Need to call add function with a=3214, b=5632.\n\u003e\n\u003e \\# Tool call\n\u003e\n\u003e {fc\\_e9b9677b-898c-46df-9deb-39122bd6c69a add {\"a\":3214,\"b\":5632} map[] {}}\n\u003e\n\u003e \\# Answer\n\u003e\n\u003e 8846\n\n\n### Text Tools (manual)\n\n[examples/txt\\_to\\_txt\\_tool-manual/main.go](examples/txt_to_txt_tool-manual/main.go): Runs a manual loop and\nruns tool calls directly.\n💡 Set [`CEREBRAS_API_KEY`](https://cloud.cerebras.ai/platform/).\n\nSnippet:\n\n```go\n\tres, _ := c.GenSync(ctx, msgs, \u0026opts)\n\t// Add the assistant's message to the messages list.\n\tmsgs = append(msgs, res.Message)\n\t// Process the tool call from the assistant.\n\tmsg, _ := res.DoToolCalls(ctx, opts.Tools)\n\t// Add the tool call response to the messages list.\n\tmsgs = append(msgs, msg)\n\t// Follow up so the LLM can interpret the tool call response.\n\tres, _ = c.GenSync(ctx, msgs, \u0026opts)\n```\n\n\n### Text Decode reply as a struct ⚙\n\n[examples/txt\\_to\\_txt\\_decode-json/main.go](examples/txt_to_txt_decode-json/main.go): Tell the LLM to use\na specific Go struct to determine the JSON schema to generate the response. This is much more lightweight than\ntool calling!\n\nIt is very useful when we want the LLM to make a choice between values, to return a number or a boolean\n(true/false). Enums are supported.\n💡 Set [`OPENAI_API_KEY`](https://platform.openai.com/settings/organization/api-keys).\n\nSnippet:\n\n```go\n\tmsgs := genai.Messages{\n\t\tgenai.NewTextMessage(\"Is a circle round? Reply as JSON.\"),\n\t}\n\tvar circle struct {\n\t\tRound bool `json:\"round\"`\n\t}\n\topts := genai.GenOptionText{DecodeAs: \u0026circle}\n\tres, _ := c.GenSync(ctx, msgs, \u0026opts)\n\tres.Decode(\u0026circle)\n\tfmt.Printf(\"Round: %v\\n\", circle.Round)\n```\n\nThis will print:\n\n\u003e Round: true\n\n\n### Text to Image 📸\n\n[examples/txt\\_to\\_img/main.go](examples/txt_to_img/main.go): Use Together.AI's free (!) image generation\nalbeit with\nlow rate limit.\n\nSome providers return an URL that must be fetched manually within a few minutes or hours, some return the data\ninline. This example handles both cases.\n💡 Set [`TOGETHER_API_KEY`](https://api.together.ai/settings/api-keys).\n\nSnippet:\n\n```go\n\tmsgs := genai.Messages{\n\t\tgenai.NewTextMessage(\"Carton drawing of a husky playing on the beach.\"),\n\t}\n\tresult, _ := c.GenSync(ctx, msgs)\n\tfor _, r := range result.Replies {\n\t\tif r.Doc.IsZero() {\n\t\t\tcontinue\n\t\t}\n\t\t// The image can be returned as an URL or inline, depending on the provider.\n\t\tvar src io.Reader\n\t\tif r.Doc.URL != \"\" {\n\t\t\treq, _ := c.HTTPClient().Get(r.Doc.URL)\n\t\t\tsrc = req.Body\n\t\t\tdefer req.Body.Close()\n\t\t} else {\n\t\t\tsrc = r.Doc.Src\n\t\t}\n\t\tb, _ := io.ReadAll(src)\n\t\tos.WriteFile(r.Doc.GetFilename(), b, 0o644)\n\t}\n```\n\nTry it locally:\n\n```bash\ngo run github.com/maruel/genai/examples/txt_to_img@latest\n```\n\nThis may generate:\n\n\u003e ![content.jpg](https://raw.githubusercontent.com/wiki/maruel/genai/content.jpg)\n\nThis generated picture shows a fake signature. I decided to keep this example as a reminder that the result\ncomes from the data harvested that was created by real humans.\n\n\n### Image-Text to Video 🎥\n\n[examples/img-txt\\_to\\_vid/main.go](examples/img-txt_to_vid/main.go): Leverage the content.jpg file generated in\ntxt\\_to\\_img example to ask Veo 3 from Google to generate a video based on the image.\n💡 Set [`GEMINI_API_KEY`](https://aistudio.google.com/apikey).\n\nSnippet:\n\n```go\n\t// Warning: this is expensive.\n\tc, _ := gemini.New(ctx, genai.ProviderOptionModel(\"veo-3.0-fast-generate-preview\"))\n\tf, _ := os.Open(\"content.jpg\")\n\tdefer f.Close()\n\tmsgs := genai.Messages{\n\t\tgenai.Message{Requests: []genai.Request{\n\t\t\t{Text: \"Carton drawing of a husky playing on the beach.\"},\n\t\t\t{Doc: genai.Doc{Src: f}},\n\t\t}},\n\t}\n\tres, _ := c.GenSync(ctx, msgs)\n\t// Save the file in Replies like in the previous example ...\n```\n\nTry it locally:\n\n```bash\ngo run github.com/maruel/genai/examples/img-txt_to_vid@latest\n```\n\nThis may generate:\n\n\u003e ![content.avif](https://raw.githubusercontent.com/wiki/maruel/genai/content.avif)\n\n⚠ The MP4 has been recompressed to AVIF via\n[compress.sh](https://raw.githubusercontent.com/wiki/maruel/genai/compress.sh) so GitHub can render it. The\ndrawback is that audio is lost. View the original MP4 with audio (!) at\n[content.mp4](https://raw.githubusercontent.com/wiki/maruel/genai/content.mp4). May not work on Safari.\n\nThis is very impressive, but also very expensive.\n\n\n### Image-Text to Image 🖌\n\n[examples/img-txt\\_to\\_img/main.go](examples/img-txt_to_img/main.go): Edit an image with a prompt. Leverage\nthe content.jpg file generated in txt\\_to\\_img example.\n💡 Set [`BFL_API_KEY`](https://dashboard.bfl.ai/keys).\n\n```bash\ngo run github.com/maruel/genai/examples/img-txt_to_img@latest\n```\n\nThis may generate:\n\n\u003e ![content2.jpg](https://raw.githubusercontent.com/wiki/maruel/genai/content2.jpg)\n\n\n### Image-Text to Image-Text 🍌\n\n[examples/img-txt\\_to\\_img-txt/main.go](examples/img-txt_to_img-txt/main.go): Leverage the\ncontent.jpg file generated in txt\\_to\\_img example to ask gemini-2.5-flash-image-preview to change the image\nwith a prompt and ask the model to explain what it did.\n💡 Set [`GEMINI_API_KEY`](https://aistudio.google.com/apikey).\n\nSnippet:\n\n```go\n\t// Warning: This is a bit expensive.\n\tc, _ := gemini.New(ctx,\n\t\tgenai.ProviderOptionModel(\"gemini-2.5-flash-image-preview\"),\n\t\tgenai.ProviderOptionModalities{genai.ModalityImage, genai.ModalityText},\n\t)\n\t// ...\n\tres, _ := c.GenSync(ctx, msgs, \u0026gemini.GenOption{ReasoningBudget: 0})\n```\n\nTry it locally:\n\n```bash\ngo run github.com/maruel/genai/examples/img-txt_to_img-txt@latest\n```\n\nThis may generate:\n\n\u003e Of course! Here's an updated image with more animals. I added a playful dolphin jumping out of the water and\n\u003e a flock of seagulls flying overhead. I chose these animals to enhance the beach scene and create a more\n\u003e dynamic and lively atmosphere. \n\u003e\n\u003e Wrote: content.png\n\u003e\n\u003e ![content.png](https://raw.githubusercontent.com/wiki/maruel/genai/content.png)\n\nThis is quite impressive, but also quite expensive.\n\n\n### Image-Text to Text 👁\n\n[examples/img-txt\\_to\\_txt/main.go](examples/img-txt_to_txt/main.go): Run vision to analyze a picture provided\nas an URL (source: [wikipedia](https://en.m.wikipedia.org/wiki/File:Banana-Single.jpg)). The response is\nstreamed out the console as the reply is generated.\n💡 Set [`MISTRAL_API_KEY`](https://console.mistral.ai/api-keys).\n\n```bash\ngo run github.com/maruel/genai/examples/img-txt_to_txt@latest\n```\n\nThis may generate:\n\n\u003e The image depicts a single ripe banana. It has a bright yellow peel with a few small brown spots, indicating\n\u003e ripeness. The banana is curved, which is typical of its natural shape, and it has a stem at the top. The\n\u003e overall appearance suggests that it is ready to be eaten.\n\n\n### Image-Text to Text (local) 🏠\n\n[examples/img-txt_to_txt_local/main.go](examples/img-txt_to_txt_local/main.go): is very similar to the\nprevious example!\n\nUse [cmd/llama-serve](cmd/llama-serve) to run a LLM locally, including tool calling and vision!\n\nStart llama-server locally either by yourself or with this utility:\n\n```bash\ngo run github.com/maruel/genai/cmd/llama-serve@latest \\\n  -model ggml-org/gemma-3-4b-it-GGUF/gemma-3-4b-it-Q8_0.gguf#mmproj-model-f16.gguf -- \\\n  --temp 1.0 --top-p 0.95 --top-k 64 \\\n  --jinja -fa -c 0 --no-warmup\n```\n\nRun vision 100% locally on CPU with only 8GB of RAM. No GPU required!\n\n```bash\ngo run github.com/maruel/genai/examples/img-txt_to_txt_local@latest\n```\n\n\n### Video-Text to Text 🎞️\n\n[examples/vid-txt\\_to\\_txt/main.go](examples/vid-txt_to_txt/main.go): Run vision to analyze a video.\n💡 Set [`GEMINI_API_KEY`](https://aistudio.google.com/apikey).\n\nUsing this video:\n\n![video.avif](https://raw.githubusercontent.com/wiki/maruel/genai/video.avif)\n\nTry it locally:\n\n```bash\ngo run github.com/maruel/genai/examples/vid-txt_to_txt@latest\n```\n\nWhen asked _What is the word_, this generates:\n\n\u003e Banana\n\n\n### Audio-Text to Text 🎤\n\n[examples/aud-txt\\_to\\_txt/main.go](examples/aud-txt_to_txt/main.go):\nAnalyze an audio [file](https://github.com/maruel/genai/raw/refs/heads/main/scoreboard/testdata/audio.mp3).\n💡 Set [`OPENAI_API_KEY`](https://platform.openai.com/settings/organization/api-keys).\n\n\nTry it locally:\n\n```bash\ngo run github.com/maruel/genai/examples/vid-txt_to_txt@latest\n```\n\nWhen asked _What was the word?_, this generates:\n\n\u003e The word was \"orange.\"\n\n\n### Usage and Quota 🍟🧀🥣\n\n[examples/txt\\_to\\_txt\\_quota/main.go](examples/txt_to_txt_quota/main.go): Prints the tokens processed and\ngenerated for the request and the remaining quota if the provider supports it.\n💡 Set [`GROQ_API_KEY`](https://console.groq.com/keys).\n\nSnippet:\n\n```go\n\tmsgs := genai.Messages{\n\t\tgenai.NewTextMessage(\"Describe poutine as a French person who just arrived in Québec\"),\n\t}\n\tres, _ := c.GenSync(ctx, msgs)\n\tfmt.Println(res.String())\n\tfmt.Printf(\"\\nTokens usage: %s\\n\", res.Usage.String())\n```\n\nThis may generate:\n\n\u003e **« Je viens tout juste d’arriver au Québec et, pour être honnête, je n’avais jamais entendu parler du\n\u003e fameux « poutine » avant de mettre le pied dans un petit resto du coin. »**\n\u003e\n\u003e (...)\n\u003e\n\u003e Tokens usage: in: 83 (cached 0), reasoning: 0, out: 818, total: 901, requests/2025-08-29 15:58:13:\n\u003e 499999/500000, tokens/2025-08-29 15:58:12: 249916/250000\n\nIn addition to the token usage, remaining quota is printed.\n\n\n### Text with any provider ⁉\n\n[examples/txt\\_to\\_txt\\_any/main.go](examples/txt_to_txt_any/main.go): Let the user chose the provider by name.\n\nThe relevant environment variable (e.g. `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, etc) is used automatically for\nauthentication.\n\nAutomatically selects a models on behalf of the user. Wraps the explicit thinking tokens if needed.\n\nSupports [ollama](https://ollama.com/) and [llama-server](https://github.com/ggml-org/llama.cpp) even if they\nrun on a remote host or non-default port.\n\nSnippet:\n\n```go\n\tnames := strings.Join(slices.Sorted(maps.Keys(providers.Available(ctx))), \", \")\n\tprovider := flag.String(\"provider\", \"\", \"provider to use, \"+names)\n\tflag.Parse()\n\n\tcfg := providers.All[*provider]\n\tc, _ := cfg.Factory(ctx, genai.ModelGood)\n\tp := adapters.WrapReasoning(c)\n\tres, _ := p.GenSync(...)\n```\n\nTry it locally:\n\n```bash\ngo run github.com/maruel/genai/examples/txt_to_txt_any@latest \\\n    -provider cerebras \\\n    \"Tell a good sounding advice that is a bad idea in practice.\"\n```\n\n\n## Model Selection\n\nFor automatic model selection, pass one of the marker constants directly:\n\n```go\n// Automatic selection - the provider picks the best model for the tier\nc, _ := anthropic.New(ctx, genai.ModelCheap) // Cheapest model\nc, _ := anthropic.New(ctx, genai.ModelGood)  // Good everyday model (recommended)\nc, _ := anthropic.New(ctx, genai.ModelSOTA)  // State-of-the-art model\n```\n\nFor a specific model, wrap the model ID with `ProviderOptionModel`:\n\n```go\n// Specific model selection\nc, _ := anthropic.New(ctx, genai.ProviderOptionModel(\"claude-sonnet-4-20250514\"))\nc, _ := gemini.New(ctx, genai.ProviderOptionModel(\"gemini-2.5-flash\"))\n```\n\n\n## Models 🗒\n\nSnapshot of all the supported models at [docs/MODELS.md](docs/MODELS.md) is updated weekly.\n\nTry it locally:\n\n```bash\ngo install github.com/maruel/genai/cmd/...@latest\n\nlist-models -provider huggingface\n```\n\n\n## Providers with free tier 💸\n\nAs of August 2025, the following services offer a free tier (other limits\napply):\n\n- [Cerebras](https://cerebras.ai/inference) has unspecified \"generous\" free tier\n- [Cloudflare Workers AI](https://developers.cloudflare.com/workers-ai/platform/pricing/) about 10k tokens/day\n- [Cohere](https://docs.cohere.com/docs/rate-limits) (1000 RPCs/month)\n- [Google's Gemini](https://ai.google.dev/gemini-api/docs/rate-limits) 0.25qps, 1m tokens/month\n- [Groq](https://console.groq.com/docs/rate-limits) 0.5qps, 500k tokens/day\n- [HuggingFace](https://huggingface.co/docs/api-inference/pricing) 10¢/month\n- [Mistral](https://help.mistral.ai/en/articles/225174-what-are-the-limits-of-the-free-tier) 1qps, 1B tokens/month\n- [Pollinations.ai](https://api.together.ai/settings/plans) provides many models for free, including image\n  generation\n- [Together.AI](https://api.together.ai/settings/plans) provides many models for free at 1qps, including image\n  generation\n- Running [Ollama](https://ollama.com/) or [llama.cpp](https://github.com/ggml-org/llama.cpp) locally is free. :)\n\n\n## TODO\n\nPRs are appreciated for any of the following. No need to ask! Just send a PR and make it pass CI checks. ❤️\n\n### Features\n\n- [ ] Authentication: OAuth, service account, OIDC,\n  [GITHUB_TOKEN](https://docs.github.com/en/github-models/use-github-models/integrating-ai-models-into-your-development-workflow#using-ai-models-with-github-actions).\n- [ ] Server-side MCP Client: [OpenAI](https://platform.openai.com/docs/guides/tools-remote-mcp)\n  - [x] [Anthropic](https://docs.anthropic.com/en/docs/agents-and-tools/mcp-connector) raw API is implemented\n    and smoke tested but there's no abstraction layer yet\n- [ ] Real-time / Live: [Gemini](https://ai.google.dev/api/live),\n  [OpenAI](https://platform.openai.com/docs/guides/realtime),\n  [TogetherAI](https://docs.together.ai/docs/text-to-speech), ...\n- [ ] More comprehensive file/cache abstraction\n- [ ] Tokens counting: [Anthropic](https://docs.anthropic.com/en/docs/build-with-claude/token-counting),\n  [Cohere](https://docs.cohere.com/reference/tokenize), [Gemini](https://ai.google.dev/api/tokens), ...\n- [ ] Embeddings: [Anthropic](https://docs.anthropic.com/en/docs/build-with-claude/embeddings),\n  [Cohere](https://docs.cohere.com/reference/embed), [Gemini](https://ai.google.dev/api/embeddings),\n  [OpenAI](https://platform.openai.com/docs/guides/embeddings), [TogetherAI](https://docs.together.ai/docs/embeddings-overview), ...\n- [ ] Image to 3D, e.g. [github.com/Tencent-Hunyuan/Hunyuan3D-2](https://github.com/Tencent-Hunyuan/Hunyuan3D-2)\n\n### Providers\n\nI'd be delighted if you want to contribute any missing provider being added, I'm particularly looking forward to these:\n\n- [ ] [Alibaba Cloud](https://www.alibabacloud.com/): Maker of Qwen models.\n- [ ] [AWS Bedrock](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_Operations_Amazon_Bedrock.html)\n- [ ] [Azure AI](https://learn.microsoft.com/en-us/rest/api/aifoundry/model-inference/get-chat-completions/get-chat-completions)\n- [ ] [Fireworks responses](https://fireworks.ai/docs/guides/response-api)\n- [ ] [GitHub](https://docs.github.com/en/rest/models/inference) inference API, which works on GitHub Actions (!)\n- [ ] [Google's Vertex AI](https://cloud.google.com/vertex-ai/docs/reference/rest): It supports much more\n  features than Gemini API.\n- [ ] Groq\n    - [ ] [Speech to Text (STT)](https://console.groq.com/docs/speech-to-text)\n    - [ ] [Text to Speech (TTS)](https://console.groq.com/docs/text-to-speech)\n    - [ ] [Batch](https://console.groq.com/docs/batch)\n- [ ] [LM Studio](https://lmstudio.ai/): Easier way to run local models.\n- [ ] Mistral\n    - [ ]\n      [Batch](https://docs.mistral.ai/api/#tag/models/operation/jobs_api_routes_fine_tuning_unarchive_fine_tuned_model)\n    - [ ] [Fill in the Middle\n      (FIM)](https://docs.mistral.ai/api/#tag/chat/operation/chat_completion_v1_chat_completions_post)\n    - [ ] [Transcription](https://docs.mistral.ai/api/#tag/ocr/operation/ocr_v1_ocr_post)\n- [ ] [Novita](https://novita.ai/): Supports lots of modalities.\n- [ ] [Open Router](https://openrouter.ai/)\n- [ ] OpenAI\n    - [ ] [Audio](https://platform.openai.com/docs/api-reference/audio/createSpeech)\n    - [ ] [Batch Responses API](https://platform.openai.com/docs/api-reference/batch)\n    - [ ] [Files](https://platform.openai.com/docs/api-reference/files/create)\n    - [ ] [Image streaming](https://platform.openai.com/docs/api-reference/images-streaming/image_generation)\n- [ ] [Runway](https://docs.dev.runwayml.com/api-details/sdks/): Specialized in images and videos.\n- [ ] [Synexai](https://synexa.ai): It's very cheap.\n- [ ] [vLLM](https://docs.vllm.ai/): The fastest way to run local models.\n\nI'm also looking to disconnect more the scoreboard from the Go code. I believe the scoreboard is useful in\nitself and is not Go specific. I appreciate ideas towards achieving this, send them my way!\n\nThanks in advance! 🙏\n\nMade with ❤️ by [Marc-Antoine Ruel](https://maruel.ca)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaruel%2Fgenai","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaruel%2Fgenai","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaruel%2Fgenai/lists"}