{"id":30910234,"url":"https://github.com/pnvang/llm-fillin","last_synced_at":"2026-04-20T19:03:14.218Z","repository":{"id":312957406,"uuid":"1049449258","full_name":"pnvang/llm-fillin","owner":"pnvang","description":"LLM-powered slot filling and orchestration for Ruby. Register tools, validate input, and execute backend actions safely.","archived":false,"fork":false,"pushed_at":"2025-09-03T02:50:34.000Z","size":26,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-13T17:18:33.637Z","etag":null,"topics":["ai","assistant","gem","json-schema","llm","openai","orchestration","ruby","slot-filling"],"latest_commit_sha":null,"homepage":"https://rubygems.org/gems/llm-fillin","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pnvang.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":null,"dco":null,"cla":null}},"created_at":"2025-09-03T02:04:12.000Z","updated_at":"2025-09-03T02:50:37.000Z","dependencies_parsed_at":"2025-09-03T04:09:19.409Z","dependency_job_id":"c6fd90e6-6a88-483a-8b41-16f9b9e1be3e","html_url":"https://github.com/pnvang/llm-fillin","commit_stats":null,"previous_names":["pnvang/llm-fillin"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/pnvang/llm-fillin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pnvang%2Fllm-fillin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pnvang%2Fllm-fillin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pnvang%2Fllm-fillin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pnvang%2Fllm-fillin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pnvang","download_url":"https://codeload.github.com/pnvang/llm-fillin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pnvang%2Fllm-fillin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32061251,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T11:35:06.609Z","status":"ssl_error","status_checked_at":"2026-04-20T11:34:48.899Z","response_time":94,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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","assistant","gem","json-schema","llm","openai","orchestration","ruby","slot-filling"],"created_at":"2025-09-09T17:03:19.191Z","updated_at":"2026-04-20T19:03:14.213Z","avatar_url":"https://github.com/pnvang.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# llm-fillin\n\n**LLM-powered slot filling + tool orchestration for Ruby.**  \nTurn natural language into structured, validated function calls.  \n`llm-fillin` provides the glue between an LLM and your backend services.\n\n---\n\n## Install\n```bash\nbundle install\n```\n\n## Configure\nSet your OpenAI API key:\n```bash\nexport OPENAI_API_KEY=sk-...\n```\n\n## Run the demo\n```bash\nruby examples/run_create_toy.rb\n```\n\n---\n\n## Example Conversation\n\n```text\nAI: Got it! You want a red race car toy for $12. Could you please provide a name for this toy?\nYOU: Supra\nAI: You want a red race car toy named \"Supra\" for $12. Just to confirm, is the category of the toy \"car\"?\nYOU: what other category are there?\nAI: The toy categories available are plush, puzzle, doll, car, lego, and other. Would you like to proceed with the \"car\" category for the red race car toy named Supra?\nYOU: make it lego\nAI: You want a red Lego race car toy priced at $12. Just to confirm, is the toy name \"Supra\"?\nYOU: yes\n✅ Toy created: Supra (lego, red) - $12.0 | ID TOY-6A99ED\n```\n\n---\n\n## Quick Start (using as a gem)\n\nOnce you’ve installed the gem:\n\n```ruby\ngem \"llm-fillin\"\n```\n\nand bundled:\n\n```bash\nbundle install\n```\n\nYou can use it in your app like this:\n\n```ruby\nrequire \"llm/fillin\"\nrequire \"json\"\n\n# 1. Create a registry and register your tools\nregistry = LLM::Fillin::Registry.new\n\nCREATE_TOY_V1 = {\n  \"type\" =\u003e \"object\",\n  \"properties\" =\u003e {\n    \"name\"     =\u003e { \"type\" =\u003e \"string\" },\n    \"category\" =\u003e { \"type\" =\u003e \"string\", \"enum\" =\u003e %w[plush puzzle doll car lego other] },\n    \"price\"    =\u003e { \"type\" =\u003e \"integer\" },\n    \"color\"    =\u003e { \"type\" =\u003e \"string\" }\n  },\n  \"required\" =\u003e %w[name category price color]\n}\n\nregistry.register!(\n  name: \"create_toy\", version: \"v1\",\n  schema: CREATE_TOY_V1,\n  description: \"Create a toy with name, category, price, and color.\",\n  handler: -\u003e(args, ctx) {\n    key = \"chat-#{ctx[:thread_id]}-#{SecureRandom.hex(6)}\"\n    { id: \"TOY-#{SecureRandom.hex(4)}\", idempotency_key: key }.merge(args)\n  }\n)\n\n# 2. Set up the adapter, store, and orchestrator\nadapter = LLM::Fillin::OpenAIAdapter.new(\n  api_key: ENV.fetch(\"OPENAI_API_KEY\"),\n  model: \"gpt-4o-mini\"\n)\n\nstore = LLM::Fillin::StoreMemory.new\norchestrator = LLM::Fillin::Orchestrator.new(\n  adapter: adapter,\n  registry: registry,\n  store: store\n)\n\n# 3. Send messages through the orchestrator\nthread_id = \"demo-thread\"\ntenant_id = \"demo-tenant\"\nactor_id  = \"demo-user\"\n\nloop do\n  print \"YOU: \"\n  user_input = STDIN.gets.strip\n  break if user_input.empty?\n\n  res = orchestrator.step(\n    thread_id: thread_id,\n    tenant_id: tenant_id,\n    actor_id: actor_id,\n    messages: [{ role: \"user\", content: user_input }]\n  )\n\n  case res[:type]\n  when :assistant\n    puts \"AI: #{res[:text]}\"\n  when :tool_ran\n    puts \"✅ Tool ran: #{res[:tool_name]} =\u003e #{res[:result].to_json}\"\n  end\nend\n```\n\n### Example Run\n```text\nYOU: create me a toy for $15\nAI: Got it! What’s the name of the toy?\nYOU: Pikachu\nAI: Great, should the category be plush, puzzle, doll, car, lego, or other?\nYOU: plush\nAI: And what color should Pikachu be?\nYOU: yellow\n✅ Tool ran: create_toy =\u003e {\"id\":\"TOY-5A9F\",\"idempotency_key\":\"chat-demo-thread-...\",\"name\":\"Pikachu\",\"category\":\"plush\",\"price\":1500,\"color\":\"yellow\"}\n```\n\n---\n\n## How it works (Architecture)\n\nWhen you interact with the assistant, several components collaborate:\n\n### 1) Registry\nThe `Registry` is where tools (functions) are registered so the LLM can call them.  \nEach tool has:\n- `name` + `version`\n- `schema` (JSON Schema that defines the inputs)\n- `description` (human-readable prompt for the LLM)\n- `handler` (Ruby lambda that executes the action)\n\n**Example**\n```ruby\nregistry.register!(\n  name: \"create_toy\", version: \"v1\",\n  schema: CREATE_TOY_V1,\n  description: \"Create a toy with name, category, price, and color.\",\n  handler: -\u003e(args, ctx) { Toy.create!(args.merge(ctx)) }\n)\n```\n\n### 2) Validators\nAll arguments are validated with [`json_schemer`](https://github.com/davishmcclurg/json_schemer) before a handler is called:\n- Required fields must exist\n- Types must match (string, integer, enum, etc.)\n- No unexpected fields are allowed\n\nThis keeps your backend safe.\n\n### 3) Idempotency\nWhen creating resources, an idempotency key is generated with:\n```ruby\n\"chat-\u003cthread_id\u003e-#{SecureRandom.hex(6)}\"\n```\nThis ensures the same request doesn’t create duplicates.\n\n### 4) Orchestrator\nThe orchestrator coordinates everything:\n- Supplies registered tools to the LLM\n- Tracks conversation state and tool results\n- Parses LLM output (text vs. function call)\n- Validates arguments and calls the handler\n- Stores tool results in memory\n\nThis is what lets the AI ask for missing fields naturally.\n\n### 5) Adapters\nAdapters handle communication with the LLM.  \nCurrently there’s an `OpenAIAdapter` that:\n- Sends prompts, tools, and messages to OpenAI\n- Parses responses (`tool_calls` or legacy `function_call`)\n- Wraps tool results for reuse in the conversation\n\nOther adapters could be added later for Anthropic, Mistral, or local LLMs.\n\n### 6) StoreMemory\nA simple in-memory store for tool results by thread.  \nIn production, this could be replaced by Redis or a database.\n\n---\n\n## End-to-End Flow\n1. **User says**: `\"I want a red race car toy for $12\"`.\n2. **LLM identifies intent**: `\"create_toy_v1\"`.\n3. **LLM fills slots**: price = 1200, color = red. Missing: name, category.\n4. **LLM asks user**: “What’s the name?” → “Supra”.\n5. **LLM asks user**: “Is the category car?” → “make it lego”.\n6. **Arguments validated** by `Validators`.\n7. **Handler executes** with args + context + idempotency key.\n8. **Result returned**: structured hash (toy ID, name, category, etc.).\n9. **Assistant replies**: ✅ Toy created.\n\n---\n\n## Use in your app\n- Define tools (schemas + handlers).\n- Register them in the `Registry`.\n- Wire the `Orchestrator` with an `Adapter` and a `Store`.\n- Pass messages into `orchestrator.step`.\n- Handle either assistant replies or tool execution results.\n\n---\n\n## License\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpnvang%2Fllm-fillin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpnvang%2Fllm-fillin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpnvang%2Fllm-fillin/lists"}