{"id":32372088,"url":"https://github.com/devlooped/ai","last_synced_at":"2025-10-24T21:50:01.978Z","repository":{"id":288155435,"uuid":"966362773","full_name":"devlooped/AI","owner":"devlooped","description":"Extensions for Microsoft.Agents.AI and Microsoft.Extensions.AI","archived":false,"fork":false,"pushed_at":"2025-10-23T19:55:21.000Z","size":404,"stargazers_count":3,"open_issues_count":2,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-23T21:35:07.492Z","etag":null,"topics":["agents","agents-sdk","ai","dotnet","extensions"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devlooped.png","metadata":{"files":{"readme":"readme.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"license.txt","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":null,"dco":null,"cla":null},"funding":{"github":"devlooped"}},"created_at":"2025-04-14T20:05:06.000Z","updated_at":"2025-10-23T19:55:25.000Z","dependencies_parsed_at":null,"dependency_job_id":"aedd80f6-9855-4ae4-8cba-ab44d78828c6","html_url":"https://github.com/devlooped/AI","commit_stats":null,"previous_names":["devlooped/extensions.ai","devlooped/ai"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/devlooped/AI","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlooped%2FAI","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlooped%2FAI/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlooped%2FAI/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlooped%2FAI/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devlooped","download_url":"https://codeload.github.com/devlooped/AI/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlooped%2FAI/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280872028,"owners_count":26405606,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-24T02:00:06.418Z","response_time":73,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["agents","agents-sdk","ai","dotnet","extensions"],"created_at":"2025-10-24T21:50:00.480Z","updated_at":"2025-10-24T21:50:01.962Z","avatar_url":"https://github.com/devlooped.png","language":"C#","funding_links":["https://github.com/sponsors/devlooped","https://github.com/sponsors"],"categories":[],"sub_categories":[],"readme":"![Icon](assets/img/icon-32.png) Devlooped AI Extensions\n============\n\n[![EULA](https://img.shields.io/badge/EULA-OSMF-blue?labelColor=black\u0026color=C9FF30)](osmfeula.txt)\n[![OSS](https://img.shields.io/github/license/devlooped/oss.svg?color=blue)](license.txt) \n\nExtensions for Microsoft.Agents.AI and Microsoft.Extensions.AI.\n\n\u003c!-- include https://github.com/devlooped/.github/raw/main/osmf.md --\u003e\n## Open Source Maintenance Fee\n\nTo ensure the long-term sustainability of this project, users of this package who generate \nrevenue must pay an [Open Source Maintenance Fee](https://opensourcemaintenancefee.org). \nWhile the source code is freely available under the terms of the [License](license.txt), \nthis package and other aspects of the project require [adherence to the Maintenance Fee](osmfeula.txt).\n\nTo pay the Maintenance Fee, [become a Sponsor](https://github.com/sponsors/devlooped) at the proper \nOSMF tier. A single fee covers all of [Devlooped packages](https://www.nuget.org/profiles/Devlooped).\n\n\u003c!-- https://github.com/devlooped/.github/raw/main/osmf.md --\u003e\n\n# Devlooped.Agents.AI\n\n[![Version](https://img.shields.io/nuget/vpre/Devlooped.Agents.AI.svg?color=royalblue)](https://www.nuget.org/packages/Devlooped.Agents.AI)\n[![Downloads](https://img.shields.io/nuget/dt/Devlooped.Agents.AI.svg?color=green)](https://www.nuget.org/packages/Devlooped.Agents.AI)\n\n\u003c!-- #agents-title --\u003e\nExtensions for Microsoft.Agents.AI, such as configuration-driven auto-reloading agents.\n\u003c!-- #agents-title --\u003e\n\n\u003c!-- #agents --\u003e\n## Overview\n\nMicrosoft.Agents.AI (aka [Agent Framework](https://learn.microsoft.com/en-us/agent-framework/overview/agent-framework-overview) \nis a comprehensive API for building AI agents. Its programatic model (which follows closely \nthe [Microsoft.Extensions.AI](https://learn.microsoft.com/en-us/dotnet/ai/microsoft-extensions-ai) \napproach) provides maximum flexibility with little prescriptive structure.\n\nThis package provides additional extensions to make developing agents easier and more \ndeclarative.\n\n## Configurable Agents\n\nTweaking agent options such as description, instructions, chat client to use and its \noptions, etc. is very common during development/testing. This package provides the ability to \ndrive those settings from configuration (with auto-reload support). This makes it far easier \nto experiment with various combinations of agent instructions, chat client providers and \noptions, and model parameters without changing code, recompiling or even restarting the application:\n\n\u003e [!NOTE]\n\u003e This example shows integration with configurable chat clients feature from the \n\u003e Devlooped.Extensions.AI package, but any `IChatClient` registered in the DI container \n\u003e with a matching key can be used.\n\n```json\n{\n  \"AI\": {\n    \"Agents\": {\n      \"MyAgent\": {\n        \"Description\": \"An AI agent that helps with customer support.\",\n        \"Instructions\": \"You are a helpful assistant for customer support.\",\n        \"Client\": \"Grok\",\n        \"Options\": {\n          \"ModelId\": \"grok-4\",\n          \"Temperature\": 0.5,\n        }\n      }\n    },\n    \"Clients\": {\n      \"Grok\": {\n        \"Endpoint\": \"https://api.grok.ai/v1\",\n        \"ModelId\": \"grok-4-fast-non-reasoning\",\n        \"ApiKey\": \"xai-asdf\"\n      }\n    }\n  }\n}\n````\n\n```csharp\nvar host = new HostApplicationBuilder(args);\nhost.Configuration.AddJsonFile(\"appsettings.json, optional: false, reloadOnChange: true);\n\n// 👇 implicitly calls AddChatClients\nhost.AddAIAgents(); \n\nvar app = host.Build();\nvar agent = app.Services.GetRequiredKeyedService\u003cAIAgent\u003e(\"MyAgent\");\n```\n\nAgents are also properly registered in the corresponding Microsoft Agent Framework \n[AgentCatalog](https://learn.microsoft.com/en-us/dotnet/api/microsoft.agents.ai.hosting.agentcatalog):\n\n```csharp\nvar catalog = app.Services.GetRequiredService\u003cAgentCatalog\u003e();\nawait foreach (AIAgent agent in catalog.GetAgentsAsync())\n{\n    var metadata = agent.GetService\u003cAIAgentMetadata\u003e();\n    Console.WriteLine($\"Agent: {agent.Name} by {metadata.ProviderName}\");\n}\n```\n\nYou can of course use any config format supported by .NET configuration, such as \nTOML which is arguably more human-friendly for hand-editing:\n\n```toml\n[ai.clients.openai]\nmodelid = \"gpt-4.1\"\n\n[ai.clients.grok]\nendpoint = \"https://api.x.ai/v1\"\nmodelid = \"grok-4-fast-non-reasoning\"\n\n[ai.agents.orders]\ndescription = \"Manage orders using catalogs for food or any other item.\"\ninstructions = \"\"\"\n\n        You are an AI agent responsible for processing orders for food or other items.\n        Your primary goals are to identify user intent, extract or request provider information, manage order data using tools and friendly responses to guide users through the ordering process.\n\n    \"\"\"\n\n# ai.clients.openai, can omit the ai.clients prefix\nclient = \"openai\"\n\n[ai.agents.reminder.options]\nmodelid = \"gpt-4o-mini\"\n```\n\nThis can be used by leveraging [Tomlyn.Extensions.Configuration](https://www.nuget.org/packages/Tomlyn.Extensions.Configuration).\n\n\u003e [!NOTE]\n\u003e This package will automatically dedent and trim start and end newlines from \n\u003e multi-line instructions and descriptions when applying the configuration, \n\u003e avoiding unnecessary tokens being used for indentation while allowing flexible \n\u003e formatting in the config file.\n\n\n\u003c!-- #agents --\u003e\n\n# Devlooped.Extensions.AI\n\n[![Version](https://img.shields.io/nuget/vpre/Devlooped.Extensions.AI.svg?color=royalblue)](https://www.nuget.org/packages/Devlooped.Extensions.AI)\n[![Downloads](https://img.shields.io/nuget/dt/Devlooped.Extensions.AI.svg?color=green)](https://www.nuget.org/packages/Devlooped.Extensions.AI)\n\n\u003c!-- #extensions-title --\u003e\nExtensions for Microsoft.Extensions.AI\n\u003c!-- #extensions-title --\u003e\n\n\u003c!-- #extensions --\u003e\n## Configurable Chat Clients\n\nSince tweaking chat options such as model identifier, reasoning effort, verbosity \nand other model settings is very common, this package provides the ability to \ndrive those settings from configuration (with auto-reload support), both per-client \nas well as per-request. This makes local development and testing much easier and \nboosts the dev loop:\n\n```json\n{\n  \"AI\": {\n    \"Clients\": {\n      \"Grok\": {\n        \"Endpoint\": \"https://api.grok.ai/v1\",\n        \"ModelId\": \"grok-4-fast-non-reasoning\",\n        \"ApiKey\": \"xai-asdf\"\n      }\n    }\n  }\n}\n````\n\n```csharp\nvar host = new HostApplicationBuilder(args);\nhost.Configuration.AddJsonFile(\"appsettings.json, optional: false, reloadOnChange: true);\nhost.AddChatClients();\n\nvar app = host.Build();\nvar grok = app.Services.GetRequiredKeyedService\u003cIChatClient\u003e(\"Grok\");\n```\n\nChanging the `appsettings.json` file will automatically update the client \nconfiguration without restarting the application.\n\n\n## Grok\n\nFull support for Grok [Live Search](https://docs.x.ai/docs/guides/live-search) \nand [Reasoning](https://docs.x.ai/docs/guides/reasoning) model options.\n\n```csharp\n// Sample X.AI client usage with .NET\nvar messages = new Chat()\n{\n    { \"system\", \"You are a highly intelligent AI assistant.\" },\n    { \"user\", \"What is 101*3?\" },\n};\n\nvar grok = new GrokChatClient(Environment.GetEnvironmentVariable(\"XAI_API_KEY\")!, \"grok-3-mini\");\n\nvar options = new GrokChatOptions\n{\n    ModelId = \"grok-3-mini-fast\",           // 👈 can override the model on the client\n    Temperature = 0.7f,\n    ReasoningEffort = ReasoningEffort.High, // 👈 or Low\n    Search = GrokSearch.Auto,               // 👈 or On/Off\n};\n\nvar response = await grok.GetResponseAsync(messages, options);\n```\n\nSearch can alternatively be configured using a regular `ChatOptions` \nand adding the `HostedWebSearchTool` to the tools collection, which \nsets the live search mode to `auto` like above:\n\n```csharp\nvar messages = new Chat()\n{\n    { \"system\", \"You are an AI assistant that knows how to search the web.\" },\n    { \"user\", \"What's Tesla stock worth today? Search X and the news for latest info.\" },\n};\n\nvar grok = new GrokChatClient(Environment.GetEnvironmentVariable(\"XAI_API_KEY\")!, \"grok-3\");\n\nvar options = new ChatOptions\n{\n    Tools = [new HostedWebSearchTool()]     // 👈 equals setting GrokSearch.Auto\n};\n\nvar response = await grok.GetResponseAsync(messages, options);\n```\n\nWe also provide an OpenAI-compatible `WebSearchTool` that can be used to restrict \nthe search to a specific country in a way that works with both Grok and OpenAI:\n\n```csharp\nvar options = new ChatOptions\n{\n    Tools = [new WebSearchTool(\"AR\")] // 👈 search in Argentina\n};\n```\n\nThis is equivalent to the following when used with a Grok client:\n```csharp\nvar options = new ChatOptions\n{\n    //                                           👇 search in Argentina\n    Tools = [new GrokSearchTool(GrokSearch.On) { Country = \"AR\" }] \n};\n```\n\n### Advanced Live Search\n\nTo configure advanced live search options, beyond the `On|Auto|Off` settings \nin `GrokChatOptions`, you can use the `GrokSearchTool` instead, which exposes \nthe full breath of [live search options](https://docs.x.ai/docs/guides/live-search) \navailable in the Grok API. \n\n```csharp\nvar options = new ChatOptions\n{\n    Tools = [new GrokSearchTool(GrokSearch.On)\n    {\n        FromDate = new DateOnly(2025, 1, 1),\n        ToDate = DateOnly.FromDateTime(DateTime.Now),\n        MaxSearchResults = 10,\n        Sources =\n        [\n            new GrokWebSource\n            {\n                AllowedWebsites =\n                [\n                    \"https://catedralaltapatagonia.com\",\n                    \"https://catedralaltapatagonia.com/parte-de-nieve/\",\n                    \"https://catedralaltapatagonia.com/tarifas/\"\n                ]\n            },\n        ]\n    }]\n};\n```\n\n\u003e [!TIP]\n\u003e You can configure multiple sources including `GrokWebSource`, `GrokNewsSource`,\n\u003e `GrokRssSource` and `GrokXSource`, each containing granular options.\n\n## OpenAI\n\nThe support for OpenAI chat clients provided in [Microsoft.Extensions.AI.OpenAI](https://www.nuget.org/packages/Microsoft.Extensions.AI.OpenAI) fall short in some scenarios:\n\n* Specifying per-chat model identifier: the OpenAI client options only allow setting \n  a single model identifier for all requests, at the time the `OpenAIClient.GetChatClient` is \n  invoked.\n* Setting reasoning effort: the Microsoft.Extensions.AI API does not expose a way to set reasoning \n  effort for reasoning-capable models, which is very useful for some models like `o4-mini`.\n\nSo solve both issues, this package provides an `OpenAIChatClient` that wraps the underlying \n`OpenAIClient` and allows setting the model identifier and reasoning effort per request, just \nlike the above Grok examples showed:\n\n```csharp\nvar messages = new Chat()\n{\n    { \"system\", \"You are a highly intelligent AI assistant.\" },\n    { \"user\", \"What is 101*3?\" },\n};\n\nIChatClient chat = new OpenAIChatClient(Environment.GetEnvironmentVariable(\"OPENAI_API_KEY\")!, \"gpt-5\");\n\nvar options = new ChatOptions\n{\n    ModelId = \"gpt-5-mini\",                 // 👈 can override the model on the client\n    ReasoningEffort = ReasoningEffort.High, // 👈 or Medium/Low/Minimal, extension property\n};\n\nvar response = await chat.GetResponseAsync(messages, options);\n```\n\n\u003e [!TIP]\n\u003e We provide support for the newest `Minimal` reasoning effort in the just-released\n\u003e GPT-5 model family.\n\n### Web Search\n\nSimilar to the Grok client, we provide the `WebSearchTool` to enable search customization \nin OpenAI too:\n\n```csharp\nvar options = new ChatOptions\n{\n    //                          👇 search in Argentina, Bariloche region\n    Tools = [new WebSearchTool(\"AR\")\n    {\n        Region = \"Bariloche\",                        // 👈 Bariloche region\n        TimeZone = \"America/Argentina/Buenos_Aires\", // 👈 IANA timezone\n        ContextSize = WebSearchToolContextSize.High      // 👈 high search context size\n    }]\n};\n```\n\n\u003e [!NOTE]\n\u003e This enables all features supported by the [Web search](https://platform.openai.com/docs/guides/tools-web-search) \n\u003e feature in OpenAI.\n\nIf advanced search settings are not needed, you can use the built-in M.E.AI `HostedWebSearchTool` \ninstead, which is a more generic tool and provides the basics out of the box.\n\n\n## Observing Request/Response\n\nThe underlying HTTP pipeline provided by the Azure SDK allows setting up \npolicies that can observe requests and responses. This is useful for \nmonitoring the requests and responses sent to the AI service, regardless \nof the chat pipeline configuration used. \n\nThis is added to the `OpenAIClientOptions` (or more properly, any \n`ClientPipelineOptions`-derived options) using the `Observe` method:\n\n```csharp\nvar openai = new OpenAIClient(\n    Environment.GetEnvironmentVariable(\"OPENAI_API_KEY\")!,\n    new OpenAIClientOptions().Observe(\n        onRequest: request =\u003e Console.WriteLine($\"Request: {request}\"),\n        onResponse: response =\u003e Console.WriteLine($\"Response: {response}\"),\n    ));\n```\n\nYou can for example trivially collect both requests and responses for \npayload analysis in tests as follows:\n\n```csharp\nvar requests = new List\u003cJsonNode\u003e();\nvar responses = new List\u003cJsonNode\u003e();\nvar openai = new OpenAIClient(\n    Environment.GetEnvironmentVariable(\"OPENAI_API_KEY\")!,\n    new OpenAIClientOptions().Observe(requests.Add, responses.Add));\n```\n\nWe also provide a shorthand factory method that creates the options \nand observes is in a single call:\n\n```csharp\nvar requests = new List\u003cJsonNode\u003e();\nvar responses = new List\u003cJsonNode\u003e();\nvar openai = new OpenAIClient(\n    Environment.GetEnvironmentVariable(\"OPENAI_API_KEY\")!,\n    OpenAIClientOptions.Observable(requests.Add, responses.Add));\n```\n\n## Tool Results\n\nGiven the following tool:\n\n```csharp\nMyResult RunTool(string name, string description, string content) { ... }\n```\n\nYou can use the `ToolFactory` and `FindCall\u003cMyResult\u003e` extension method to \nlocate the function invocation, its outcome and the typed result for inspection:\n\n```csharp\nAIFunction tool = ToolFactory.Create(RunTool);\nvar options = new ChatOptions\n{\n    ToolMode = ChatToolMode.RequireSpecific(tool.Name), // 👈 forces the tool to be used\n    Tools = [tool]\n};\n\nvar response = await client.GetResponseAsync(chat, options);\n// 👇 finds the expected result of the tool call\nvar result = response.FindCalls\u003cMyResult\u003e(tool).FirstOrDefault();\n\nif (result != null)\n{\n    // Successful tool call\n    Console.WriteLine($\"Args: '{result.Call.Arguments.Count}'\");\n    MyResult typed = result.Result;\n}\nelse\n{\n    Console.WriteLine(\"Tool call not found in response.\");\n}\n```\n\nIf the typed result is not found, you can also inspect the raw outcomes by finding \nuntyped calls to the tool and checking their `Outcome.Exception` property:\n\n```csharp\nvar result = response.FindCalls(tool).FirstOrDefault();\nif (result.Outcome.Exception is not null)\n{\n    Console.WriteLine($\"Tool call failed: {result.Outcome.Exception.Message}\");\n}\nelse\n{\n    Console.WriteLine($\"Tool call succeeded: {result.Outcome.Result}\");\n}\n```\n\n\u003e [!IMPORTANT]\n\u003e The `ToolFactory` will also automatically sanitize the tool name \n\u003e when using local functions to avoid invalid characters and honor \n\u003e its original name.\n\n## Console Logging\n\nAdditional `UseJsonConsoleLogging` extension for rich JSON-formatted console logging of AI requests \nare provided at two levels: \n\n* Chat pipeline: similar to `UseLogging`.\n* HTTP pipeline: lowest possible layer before the request is sent to the AI service, \n  can capture all requests and responses. Can also be used with other Azure SDK-based \n  clients that leverage `ClientPipelineOptions`.\n\n\u003e [!NOTE]\n\u003e Rich JSON formatting is provided by [Spectre.Console](https://spectreconsole.net/)\n\nThe HTTP pipeline logging can be enabled by calling `UseJsonConsoleLogging` on the\nclient options passed to the client constructor:\n\n```csharp\nvar openai = new OpenAIClient(\n    Environment.GetEnvironmentVariable(\"OPENAI_API_KEY\")!,\n    new OpenAIClientOptions().UseJsonConsoleLogging());\n```\n\nFor a Grok client with search-enabled, a request would look like the following:\n\n![](https://raw.githubusercontent.com/devlooped/Extensions.AI/main/assets/img/chatmessage.png)\n\nBoth alternatives receive an optional `JsonConsoleOptions` instance to configure \nthe output, including truncating or wrapping long messages, setting panel style, \nand more.\n\nThe chat pipeline logging is added similar to other pipeline extensions:\n\n```csharp\nIChatClient client = new GrokChatClient(Environment.GetEnvironmentVariable(\"XAI_API_KEY\")!, \"grok-3-mini\")\n    .AsBuilder()\n    .UseOpenTelemetry()\n    // other extensions...\n    .UseJsonConsoleLogging(new JsonConsoleOptions()\n    {\n        // Formatting options...\n        Border = BoxBorder.None,\n        WrapLength = 80,\n    })\n    .Build();\n```\n\u003c!-- #extensions --\u003e\n\n\u003c!-- include https://github.com/devlooped/sponsors/raw/main/footer.md --\u003e\n# Sponsors \n\n\u003c!-- sponsors.md --\u003e\n[![Clarius Org](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/clarius.png \"Clarius Org\")](https://github.com/clarius)\n[![MFB Technologies, Inc.](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/MFB-Technologies-Inc.png \"MFB Technologies, Inc.\")](https://github.com/MFB-Technologies-Inc)\n[![DRIVE.NET, Inc.](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/drivenet.png \"DRIVE.NET, Inc.\")](https://github.com/drivenet)\n[![Keith Pickford](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Keflon.png \"Keith Pickford\")](https://github.com/Keflon)\n[![Thomas Bolon](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/tbolon.png \"Thomas Bolon\")](https://github.com/tbolon)\n[![Kori Francis](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/kfrancis.png \"Kori Francis\")](https://github.com/kfrancis)\n[![Uno Platform](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/unoplatform.png \"Uno Platform\")](https://github.com/unoplatform)\n[![Reuben Swartz](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/rbnswartz.png \"Reuben Swartz\")](https://github.com/rbnswartz)\n[![Jacob Foshee](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/jfoshee.png \"Jacob Foshee\")](https://github.com/jfoshee)\n[![](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Mrxx99.png \"\")](https://github.com/Mrxx99)\n[![Eric Johnson](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/eajhnsn1.png \"Eric Johnson\")](https://github.com/eajhnsn1)\n[![David JENNI](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/davidjenni.png \"David JENNI\")](https://github.com/davidjenni)\n[![Jonathan ](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Jonathan-Hickey.png \"Jonathan \")](https://github.com/Jonathan-Hickey)\n[![Charley Wu](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/akunzai.png \"Charley Wu\")](https://github.com/akunzai)\n[![Ken Bonny](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/KenBonny.png \"Ken Bonny\")](https://github.com/KenBonny)\n[![Simon Cropp](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/SimonCropp.png \"Simon Cropp\")](https://github.com/SimonCropp)\n[![agileworks-eu](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/agileworks-eu.png \"agileworks-eu\")](https://github.com/agileworks-eu)\n[![Zheyu Shen](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/arsdragonfly.png \"Zheyu Shen\")](https://github.com/arsdragonfly)\n[![Vezel](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/vezel-dev.png \"Vezel\")](https://github.com/vezel-dev)\n[![ChilliCream](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/ChilliCream.png \"ChilliCream\")](https://github.com/ChilliCream)\n[![4OTC](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/4OTC.png \"4OTC\")](https://github.com/4OTC)\n[![Vincent Limo](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/v-limo.png \"Vincent Limo\")](https://github.com/v-limo)\n[![Jordan S. Jones](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/jordansjones.png \"Jordan S. Jones\")](https://github.com/jordansjones)\n[![domischell](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/DominicSchell.png \"domischell\")](https://github.com/DominicSchell)\n[![Justin Wendlandt](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/jwendl.png \"Justin Wendlandt\")](https://github.com/jwendl)\n[![Adrian Alonso](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/adalon.png \"Adrian Alonso\")](https://github.com/adalon)\n[![Michael Hagedorn](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Eule02.png \"Michael Hagedorn\")](https://github.com/Eule02)\n[![](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/henkmartijn.png \"\")](https://github.com/henkmartijn)\n[![torutek](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/torutek.png \"torutek\")](https://github.com/torutek)\n[![mccaffers](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/mccaffers.png \"mccaffers\")](https://github.com/mccaffers)\n\n\n\u003c!-- sponsors.md --\u003e\n\n[![Sponsor this project](https://raw.githubusercontent.com/devlooped/sponsors/main/sponsor.png \"Sponsor this project\")](https://github.com/sponsors/devlooped)\n\u0026nbsp;\n\n[Learn more about GitHub Sponsors](https://github.com/sponsors)\n\n\u003c!-- https://github.com/devlooped/sponsors/raw/main/footer.md --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevlooped%2Fai","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevlooped%2Fai","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevlooped%2Fai/lists"}