omni_agent 0.1.10 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +55 -0
  3. data/README.md +100 -2
  4. data/Rakefile +4 -0
  5. data/app/jobs/omni_agent/embed_job.rb +10 -0
  6. data/app/jobs/omni_agent/ingest_job.rb +12 -0
  7. data/docs/agent/callbacks.md +60 -0
  8. data/docs/agent/delegation.md +56 -0
  9. data/docs/agent/provider.md +93 -0
  10. data/docs/agent/run-entrypoints.md +81 -0
  11. data/docs/agent/streaming.md +62 -0
  12. data/docs/agent/structured-output.md +80 -0
  13. data/docs/configuration.md +68 -0
  14. data/docs/evals/caching.md +21 -0
  15. data/docs/evals/generating-and-running.md +87 -0
  16. data/docs/evals/golden-sets.md +32 -0
  17. data/docs/evals/judge-provider.md +20 -0
  18. data/docs/generators.md +143 -0
  19. data/docs/index.md +98 -0
  20. data/docs/intro.md +93 -0
  21. data/docs/mcp/authentication.md +226 -0
  22. data/docs/mcp/defining-servers.md +196 -0
  23. data/docs/mcp/transports.md +166 -0
  24. data/docs/rag.md +372 -0
  25. data/docs/stock-agents/ocr.md +52 -0
  26. data/docs/stock-agents/overview.md +91 -0
  27. data/docs/supported-providers.md +47 -0
  28. data/docs/tools.md +171 -0
  29. data/exe/omni_agent +48 -10
  30. data/lib/generators/omni_agent/chunks/chunks_generator.rb +130 -0
  31. data/lib/generators/omni_agent/embeddings/embeddings_generator.rb +72 -0
  32. data/lib/generators/omni_agent/install/install_generator.rb +78 -1
  33. data/lib/generators/omni_agent/mcp_server/mcp_server_generator.rb +4 -0
  34. data/lib/generators/omni_agent/stock_agent/stock_agent_generator.rb +73 -0
  35. data/lib/generators/omni_agent/vector_store.rb +113 -0
  36. data/lib/omni_agent/agent.rb +168 -39
  37. data/lib/omni_agent/chunkable.rb +181 -0
  38. data/lib/omni_agent/chunkers/hierarchical.rb +20 -0
  39. data/lib/omni_agent/chunkers/markdown.rb +41 -0
  40. data/lib/omni_agent/chunkers/recursive.rb +53 -0
  41. data/lib/omni_agent/chunkers.rb +31 -0
  42. data/lib/omni_agent/configuration.rb +14 -1
  43. data/lib/omni_agent/docs.rb +32 -0
  44. data/lib/omni_agent/embeddable.rb +131 -0
  45. data/lib/omni_agent/engine.rb +11 -0
  46. data/lib/omni_agent/errors.rb +15 -0
  47. data/lib/omni_agent/loaders/html.rb +21 -0
  48. data/lib/omni_agent/loaders/markdown.rb +18 -0
  49. data/lib/omni_agent/loaders/pdf.rb +25 -0
  50. data/lib/omni_agent/loaders/text.rb +15 -0
  51. data/lib/omni_agent/loaders.rb +68 -0
  52. data/lib/omni_agent/mcp/authenticator.rb +5 -2
  53. data/lib/omni_agent/mcp/oauth/access_token.rb +60 -0
  54. data/lib/omni_agent/mcp/oauth/http_client.rb +52 -0
  55. data/lib/omni_agent/mcp/oauth/introspection_verifier.rb +43 -0
  56. data/lib/omni_agent/mcp/oauth/jwt_verifier.rb +122 -0
  57. data/lib/omni_agent/mcp/oauth/resource_server.rb +166 -0
  58. data/lib/omni_agent/mcp/oauth.rb +18 -0
  59. data/lib/omni_agent/mcp/rack_app.rb +31 -7
  60. data/lib/omni_agent/mcp/server.rb +22 -5
  61. data/lib/omni_agent/providers/base.rb +14 -0
  62. data/lib/omni_agent/providers/mock.rb +89 -4
  63. data/lib/omni_agent/providers/ollama.rb +4 -0
  64. data/lib/omni_agent/providers/openai.rb +33 -2
  65. data/lib/omni_agent/providers/response.rb +6 -1
  66. data/lib/omni_agent/providers.rb +7 -0
  67. data/lib/omni_agent/schema.rb +200 -0
  68. data/lib/omni_agent/semantic_search.rb +45 -0
  69. data/lib/omni_agent/stock_agent.rb +59 -0
  70. data/lib/omni_agent/stock_agents/ocr/prompt.md.erb +6 -0
  71. data/lib/omni_agent/stock_agents/ocr.rb +70 -0
  72. data/lib/omni_agent/stock_agents.rb +26 -0
  73. data/lib/omni_agent/tool.rb +7 -107
  74. data/lib/omni_agent/version.rb +1 -1
  75. data/lib/omni_agent.rb +15 -1
  76. data/lib/tasks/omni_agent_chunks_tasks.rake +16 -0
  77. data/lib/tasks/omni_agent_embeddings_tasks.rake +15 -0
  78. metadata +55 -8
  79. data/app/assets/stylesheets/omni_agent/application.css +0 -15
  80. data/app/controllers/omni_agent/application_controller.rb +0 -4
  81. data/app/helpers/omni_agent/application_helper.rb +0 -4
  82. data/app/jobs/omni_agent/application_job.rb +0 -4
  83. data/app/mailers/omni_agent/application_mailer.rb +0 -6
  84. data/app/models/omni_agent/application_record.rb +0 -5
  85. data/app/views/layouts/omni_agent/application.html.erb +0 -17
@@ -0,0 +1,80 @@
1
+ <!-- Generated from omniagent-docs/docs/agent/structured-output.mdx by `bundle exec rake docs:sync`. Do not edit by hand. -->
2
+ <!-- Online version: https://omniagent.activetaskers.com/docs/agent/structured-output -->
3
+
4
+ # Structured Output
5
+
6
+ Declare an `output` block to make an agent return a validated object instead of free text. It uses the same DSL as a tool's [`input`](../tools.md#input-schema-dsl):
7
+
8
+ ```ruby
9
+ class InvoiceAgent < OmniAgent::Agent
10
+ use_model "gpt-4o-mini"
11
+
12
+ output do
13
+ string :invoice_number, description: "Invoice identifier as printed"
14
+ number :total, min: 0
15
+ enum :currency, values: %w[USD EUR]
16
+ array :line_items do
17
+ string :description
18
+ number :amount
19
+ end
20
+ end
21
+ end
22
+
23
+ response = InvoiceAgent.new.run(invoice_text)
24
+ response.structured_output
25
+ # => { invoice_number: "A-1042", total: 120.5, currency: "USD", line_items: [ { description: "Paper", amount: 120.5 } ] }
26
+ response.answer # the raw JSON string returned by the model
27
+ ```
28
+
29
+ ## How It Works
30
+
31
+ 1. The schema is sent to the provider with every request. OpenAI and Ollama receive it as a `json_schema` `response_format`. Providers that don't support structured output natively get the schema in the system prompt instead (see [Custom Providers](#custom-providers)).
32
+ 2. When the model gives its final answer, the JSON is parsed (a surrounding ` ```json ` fence is tolerated) and validated with the same rules as tool input: required fields, `enum` values, `min`/`max`, `min_length`/`max_length`, `pattern`, `min_items`/`max_items` and custom `validate:` procs. On top of that, every top-level value must match its declared type (`string`, `integer`, `number`, `boolean`, `array` or `hash`), so `total: "120.5"` is rejected for a `number`. Keys not declared in the schema are dropped, and `null` for an optional field is treated as absent.
33
+ 3. The result is exposed as `response.structured_output`, a hash with symbol keys. It is set before `after_generation` callbacks run, so callbacks can read it from `payload[:response]`.
34
+
35
+ Type and constraint checks apply to top-level fields; nested objects are described to the model but not validated field by field.
36
+
37
+ ## Handling Errors
38
+
39
+ If the model returns invalid JSON, the object fails validation, or a polymorphic `resolve: true` field can't be resolved, `run` raises `OmniAgent::StructuredOutputError`. The error keeps the raw model output so you can log it, retry or repair it without paying for the generation again:
40
+
41
+ ```ruby
42
+ begin
43
+ InvoiceAgent.new.run(invoice_text)
44
+ rescue OmniAgent::StructuredOutputError => e
45
+ Rails.logger.warn("Invalid invoice output: #{e.message}\n#{e.content}")
46
+ e.response # the full OmniAgent::Providers::Response
47
+ end
48
+ ```
49
+
50
+ ## Strict Mode
51
+
52
+ OpenAI's strict structured outputs are enabled automatically when the schema allows it: every field is required, every `hash` has a block, and no `min_length`/`max_length` is used. Otherwise the schema is sent with `strict: false`, so the model only treats it as guidance; in that case the schema is also added to the system prompt, and OmniAgent's validation enforces it.
53
+
54
+ ## Custom Providers
55
+
56
+ A provider receives the schema as the `output_schema:` keyword of `#chat` (an `OmniAgent::Schema`; call `#json_schema` on it) only when it declares support:
57
+
58
+ ```ruby
59
+ class MyProvider < OmniAgent::Providers::Base
60
+ def supports_structured_output?
61
+ true
62
+ end
63
+
64
+ def chat(messages:, tools: [], stream: nil, output_schema: nil, **options)
65
+ # send output_schema.json_schema in the provider's own format
66
+ end
67
+ end
68
+ ```
69
+
70
+ Providers that don't (the default in `OmniAgent::Providers::Base`) never receive `output_schema:`; the agent appends the JSON schema to the system prompt and asks for a JSON-only answer instead.
71
+
72
+ ## Inheritance
73
+
74
+ `output` is inherited by subclasses, and a subclass can declare its own `output` block to replace it. This is how [stock agents](../stock-agents/overview.md) let each project decide which fields to extract.
75
+
76
+ ## Mock Provider
77
+
78
+ With `provider :mock`, an agent that declares `output` gets placeholder JSON built from the schema (the first `enum` value, `min` for numbers, lorem ipsum for strings), so you can wire up code that reads `structured_output` before calling a real model.
79
+
80
+ The placeholders can't satisfy everything. If a field has a `pattern:` the lorem text doesn't match, a custom `validate:` proc that rejects the placeholder, or is a polymorphic field with `resolve: true`, the mock raises an `OmniAgent::Error` naming the field. In those tests, stub the provider's `#chat` response instead.
@@ -0,0 +1,68 @@
1
+ <!-- Generated from omniagent-docs/docs/configuration.mdx by `bundle exec rake docs:sync`. Do not edit by hand. -->
2
+ <!-- Online version: https://omniagent.activetaskers.com/docs/configuration -->
3
+
4
+ # Configuration
5
+
6
+ Omni Agent ships with sensible defaults but lets you configure global behavior through an initializer.
7
+
8
+ ## Global Defaults
9
+
10
+ `rails generate omni_agent:install` writes `config/initializers/omni_agent.rb` with every setting commented out at its default, so you only uncomment what you change. You can also create the file yourself:
11
+
12
+ ```ruby
13
+ OmniAgent.configure do |config|
14
+ config.default_provider = :openai
15
+ config.default_model = "gpt-4o-mini"
16
+ config.max_retries = 3
17
+ config.retry_base_delay = 0.5
18
+ config.max_tool_iterations = 10
19
+ config.max_delegation_depth = 5
20
+ config.mcp_allowed_origins = []
21
+ config.mcp_max_request_bytes = 1_048_576
22
+ config.embedding_provider = nil
23
+ config.embedding_model = nil
24
+ config.embedding_job_options = {}
25
+ config.document_loaders = {}
26
+ end
27
+ ```
28
+
29
+ * **`default_provider`**: The provider used by agents that don't declare their own via `provider` or `use_model`. Defaults to `:openai`.
30
+ * **`default_model`**: The model used when an agent doesn't override it. Defaults to `"gpt-4o-mini"`.
31
+ * **`max_retries`**: Max retry attempts for retryable provider errors (rate limits, timeouts, 5xx). Defaults to `3`.
32
+ * **`retry_base_delay`**: Base delay in seconds for retry backoff. Delay doubles per attempt (exponential backoff). Defaults to `0.5`.
33
+ * **`max_tool_iterations`**: Max number of tool-call round trips the agent loop will perform before giving up on a final response. Defaults to `10`.
34
+ * **`max_delegation_depth`**: Max nesting depth for [agent delegation](agent/delegation.md) chains before raising `OmniAgent::MaxDelegationDepthError`. Defaults to `5`.
35
+ * **`mcp_allowed_origins`**: Browser origins (Strings or Regexps) allowed to call any [MCP server](mcp/authentication.md#origin-checks) over HTTP. Requests that send any other `Origin` get `403`. Defaults to `[]`.
36
+ * **`mcp_max_request_bytes`**: Largest MCP HTTP request body accepted. Larger requests get `413`. Defaults to `1_048_576` (1 MiB).
37
+ * **`embedding_provider`**: The provider [`OmniAgent.embed`](rag.md#embeddings) uses. Defaults to `nil`, which falls back to `default_provider`.
38
+ * **`embedding_model`**: The embedding model `OmniAgent.embed` uses. Defaults to `nil`, which uses the provider's default (`text-embedding-3-small` for OpenAI, `nomic-embed-text` for Ollama).
39
+ * **`embedding_job_options`**: ActiveJob `set` options (`queue`, `priority`, `wait`, `wait_until`) for the jobs [`has_embedding later:`](rag.md#background-embedding) and [`has_chunks`](rag.md#documents) enqueue. Models can override them with `later: { ... }`. Unknown keys raise an `ArgumentError`. Defaults to `{}`.
40
+ * **`document_loaders`**: Extra [loaders](rag.md#loaders) for `has_chunks`, by name (`{ docx: DocxLoader }`). Loaders with a `handles?` method are tried before the built-in ones when detecting a file's format. Defaults to `{}`.
41
+
42
+ ## Retry Behavior
43
+
44
+ Providers can mark specific errors as retryable (e.g. the OpenAI provider retries `RateLimitError`, `InternalServerError`, and `APIConnectionError`). When a retryable error occurs, the provider retries up to `max_retries` times with exponential backoff (`retry_base_delay * 2^(attempt - 1)`). Non-retryable errors raise immediately.
45
+
46
+ ## Tool Iteration Limit
47
+
48
+ The agent loop keeps calling tools until the model returns a final response. If the model keeps requesting tools past `max_tool_iterations`, the agent raises `OmniAgent::MaxToolIterationsError`. Raise `max_tool_iterations` in your initializer if your agent legitimately needs more tool round trips.
49
+
50
+ ## Per-Agent Overrides
51
+
52
+ Any agent can override these defaults individually. See [Change an Agent's Provider or Model](agent/provider.md) for the `provider` and `use_model` DSL.
53
+
54
+ ## Registered Providers
55
+
56
+ Providers are resolved from a registry at runtime:
57
+
58
+ ```ruby
59
+ OmniAgent::Providers.registry
60
+ # => {
61
+ # openai: OmniAgent::Providers::OpenAI,
62
+ # ollama: OmniAgent::Providers::Ollama,
63
+ # mock: OmniAgent::Providers::Mock,
64
+ # mock_judge: OmniAgent::Providers::MockJudge
65
+ # }
66
+ ```
67
+
68
+ See [Supported Providers](supported-providers.md) for provider-specific setup. The `:mock` provider is useful for tests — it returns a canned response without making network calls.
@@ -0,0 +1,21 @@
1
+ <!-- Generated from omniagent-docs/docs/evals/caching.mdx by `bundle exec rake docs:sync`. Do not edit by hand. -->
2
+ <!-- Online version: https://omniagent.activetaskers.com/docs/evals/caching -->
3
+
4
+ # Caching
5
+
6
+ Eval runs are cached by default, keyed on `(agent class, run_alias, input, context)`, and stored as JSON at `OmniAgent.configuration.eval_cache_path` (default `tmp/omni_agent_eval_cache.json`). Re-running the same case — e.g. while iterating on assertions — replays the cached output instead of calling the provider again, so you don't spend tokens testing the same case twice.
7
+
8
+ ```ruby
9
+ OmniAgent.configure do |config|
10
+ config.eval_cache_enabled = true # default
11
+ config.eval_cache_path = "tmp/omni_agent_eval_cache.json" # default
12
+ end
13
+ ```
14
+
15
+ To force a fresh call without deleting the file yourself, clear the cache before running with `--fresh`:
16
+
17
+ ```bash
18
+ bundle exec omni_agent eval evals/research_agent_eval.rb --fresh
19
+ ```
20
+
21
+ The equivalent rake task takes a `fresh` arg instead: `rake "omni_agent:eval[evals/research_agent_eval.rb,fresh]"`.
@@ -0,0 +1,87 @@
1
+ <!-- Generated from omniagent-docs/docs/evals/generating-and-running.mdx by `bundle exec rake docs:sync`. Do not edit by hand. -->
2
+ <!-- Online version: https://omniagent.activetaskers.com/docs/evals/generating-and-running -->
3
+
4
+ # Generating and Running Evals
5
+
6
+ `OmniAgent::Eval` lets you test agent *quality*, not just correctness — deterministic assertions (tool calls, output matching) plus pluggable LLM-as-judge scoring.
7
+
8
+ ## Defining an Eval
9
+
10
+ ```ruby
11
+ class ResearchAgentEval < OmniAgent::Eval
12
+ agent ResearchAgent
13
+
14
+ eval_case "answers weather question" do
15
+ input "What's the weather in Paris?"
16
+ expect_tool_call :get_weather, with: { city: "Paris" }
17
+ expect_output to_include: "Paris"
18
+ end
19
+
20
+ eval_case "is polite" do
21
+ input "Tell me a joke"
22
+ judge "Is the response friendly and on-topic?", threshold: 0.7
23
+ end
24
+
25
+ eval_case "summarizes via the :summarize run alias" do
26
+ run_alias :summarize
27
+ input "Some long article text...", with: { tone: "casual" }
28
+ expect_output to_include: "summary"
29
+ end
30
+ end
31
+ ```
32
+
33
+ * **`agent`**: The `OmniAgent::Agent` subclass under test.
34
+ * **`eval_case`**: Declares one scenario. `input text, with: {}` sets the message sent to the agent; `with:` is forwarded as `context:` and bound to matching instance variables during the run (e.g. `with: { tone: "casual" }` sets `@tone`).
35
+ * **`run_alias`**: Targets a method defined via `run_aliases` (or any zero-arg run entrypoint) instead of plain `#run`. Use this when that alias renders a different prompt file (`app/agents/<agent>/<method_name>.md.erb`) and you want to eval that specific prompt.
36
+ * **`expect_tool_call`**: Asserts a tool was called, optionally with specific arguments (`with:`).
37
+ * **`expect_output`**: Asserts on the agent's final text output via `to_include:`, `to_match:`, or a custom block.
38
+ * **`judge`**: Sends the output to an LLM judge with a grading criterion — see [Judge Provider](judge-provider.md) for how it picks a provider/model.
39
+
40
+ For testing many input/expected-output pairs without writing a `eval_case` per row, see [Golden Sets](golden-sets.md).
41
+
42
+ ## Generating
43
+
44
+ ```bash
45
+ rails generate omni_agent:eval ResearchAgent
46
+ ```
47
+
48
+ Creates `evals/research_agent_eval.rb` with a starter `eval_case`.
49
+
50
+ ## Running
51
+
52
+ Run evals with the `omni_agent` CLI — works like running `rspec`:
53
+
54
+ ```bash
55
+ # run every *_eval.rb under evals/
56
+ bundle exec omni_agent eval
57
+
58
+ # run just one eval file
59
+ bundle exec omni_agent eval evals/research_agent_eval.rb
60
+
61
+ # bypass the cache for this run (see Caching)
62
+ bundle exec omni_agent eval evals/research_agent_eval.rb --fresh
63
+ ```
64
+
65
+ Run it from your Rails app root — it requires `config/environment.rb` to boot, same as `bin/rails`.
66
+
67
+ It prints a pass/fail report per case (with failure messages) and exits non-zero if any case failed:
68
+
69
+ ```text
70
+ [PASS] mentions lorem
71
+ [FAIL] mentions something the mock never says
72
+ - output "Lorem ipsum dolor sit amet, consectetur adipiscing elit." does not include "this never appears"
73
+
74
+ 1/2 cases passed
75
+ ```
76
+
77
+ Running evals calls real LLM providers — it costs money and is non-deterministic, so it is **not** wired into `bundle exec rspec` or CI. Run it manually when you want a quality check.
78
+
79
+ ### Rake task alternative
80
+
81
+ A `rake omni_agent:eval` task does the same thing, if you'd rather not use the binstub:
82
+
83
+ ```bash
84
+ bundle exec rake omni_agent:eval
85
+ bundle exec rake "omni_agent:eval[evals/research_agent_eval.rb]"
86
+ bundle exec rake "omni_agent:eval[evals/research_agent_eval.rb,fresh]"
87
+ ```
@@ -0,0 +1,32 @@
1
+ <!-- Generated from omniagent-docs/docs/evals/golden-sets.mdx by `bundle exec rake docs:sync`. Do not edit by hand. -->
2
+ <!-- Online version: https://omniagent.activetaskers.com/docs/evals/golden-sets -->
3
+
4
+ # Golden Sets
5
+
6
+ For many input/expected-output pairs, load a dataset file instead of writing a `eval_case` per row:
7
+
8
+ ```ruby
9
+ class ResearchAgentEval < OmniAgent::Eval
10
+ agent ResearchAgent
11
+
12
+ golden_set "evals/golden/research_agent.yml" do |row|
13
+ expect_output to_include: row[:expected_output]
14
+ expect_tool_call row[:tool_call][:name], with: row[:tool_call][:args]
15
+ end
16
+ end
17
+ ```
18
+
19
+ `evals/golden/research_agent.yml`:
20
+
21
+ ```yaml
22
+ - name: "paris weather"
23
+ input: "What's the weather in Paris?"
24
+ expected_output: "Paris"
25
+ tool_call: { name: get_weather, args: { city: Paris } }
26
+ - name: "tokyo weather"
27
+ input: "What's the weather in Tokyo?"
28
+ expected_output: "Tokyo"
29
+ tool_call: { name: get_weather, args: { city: Tokyo } }
30
+ ```
31
+
32
+ Each row becomes its own `eval_case` (named from `name`, or `"row <index>"` if omitted). All keys are recursively symbolized, so nested fields like `row[:tool_call][:name]` work as shown above. Rows generated from `golden_set` run through the exact same path as inline `eval_case` blocks — there's no separate reporting for them, and they're subject to the same [caching](caching.md).
@@ -0,0 +1,20 @@
1
+ <!-- Generated from omniagent-docs/docs/evals/judge-provider.mdx by `bundle exec rake docs:sync`. Do not edit by hand. -->
2
+ <!-- Online version: https://omniagent.activetaskers.com/docs/evals/judge-provider -->
3
+
4
+ # Judge Provider
5
+
6
+ `judge` resolves which provider grades the output, in this order:
7
+
8
+ 1. An explicit `provider:` (and optional `model:`) kwarg passed to `judge`.
9
+ 2. `OmniAgent.configuration.eval_judge_provider` / `eval_judge_model`, set once globally:
10
+
11
+ ```ruby
12
+ OmniAgent.configure do |config|
13
+ config.eval_judge_provider = :openai
14
+ config.eval_judge_model = "gpt-4o-mini"
15
+ end
16
+ ```
17
+
18
+ 3. Falls back to the **same provider/model the agent under test already uses** — no judge setup required to get started.
19
+
20
+ The judge sends a fixed grading prompt asking for `{"score": 0-1, "reason": "..."}` and compares `score` against `threshold` from the `judge` call (see [Generating and Running Evals](generating-and-running.md)).
@@ -0,0 +1,143 @@
1
+ <!-- Generated from omniagent-docs/docs/generators.mdx by `bundle exec rake docs:sync`. Do not edit by hand. -->
2
+ <!-- Online version: https://omniagent.activetaskers.com/docs/generators -->
3
+
4
+ # Generators
5
+
6
+ Omni Agent ships seven Rails generators to scaffold your project.
7
+
8
+ ## Install Generator
9
+
10
+ ```bash
11
+ rails generate omni_agent:install
12
+ ```
13
+
14
+ Creates the `app/agents` root directory and `config/initializers/omni_agent.rb`, which lists every [configuration](configuration.md) setting commented out at its default. An existing initializer is left untouched. Run this once per application, right after installing the gem.
15
+
16
+ It also adds an OmniAgent section to `AGENTS.md` (created if missing) and to `CLAUDE.md` (only if it already exists). The section tells coding agents such as Claude Code, Cursor or Codex to read the documentation bundled with the gem:
17
+
18
+ ```bash
19
+ bundle exec omni_agent docs # index: conventions, typical flows and every page
20
+ bundle exec omni_agent docs mcp/authentication # one page
21
+ bundle exec omni_agent docs --path # directory holding the Markdown files
22
+ ```
23
+
24
+ Running the generator again doesn't duplicate the section. Pass `--skip-agents-md` to leave both files alone.
25
+
26
+ ## Agent Generator
27
+
28
+ ```bash
29
+ rails generate omni_agent:agent ResearchAgent --model gpt-4o-mini --with-tools WeatherLookup Summarize
30
+ ```
31
+
32
+ Generates:
33
+
34
+ ```text
35
+ app/agents/
36
+ ├── research_agent.rb
37
+ └── research_agent/
38
+ ├── prompt.md.erb
39
+ └── tools/
40
+ ├── weather_lookup.rb
41
+ └── summarize.rb
42
+ ```
43
+
44
+ ### Options
45
+
46
+ * **`--model`**: Sets the model used in the generated `use_model` call. If omitted, the agent uses `OmniAgent.configuration.default_model`.
47
+ * **`--with-tools`**: Space-separated list of tool names to scaffold under `tools/`. Each is generated as a subclass of `OmniAgent::Tool` with an example `input` schema and `execute` method. If you pass `--with-tools` with no names, an `ExampleTool` is generated.
48
+
49
+ ```bash
50
+ rails generate omni_agent:agent SupportAgent --with-tools
51
+ ```
52
+
53
+ ## Stock Agent Generator
54
+
55
+ ```bash
56
+ rails generate omni_agent:stock_agent InvoiceOcr --from=ocr --fields=invoice_number:string total:number
57
+ ```
58
+
59
+ Generates `app/agents/invoice_ocr.rb`, a subclass of the chosen [stock agent](stock-agents/overview.md) with an `output` block, and an optional `app/agents/invoice_ocr/prompt.md.erb` for extra instructions.
60
+
61
+ ### Options
62
+
63
+ * **`--from`** (required): Stock agent to build on, by its `stock_name` (see `OmniAgent::StockAgents.all`).
64
+ * **`--fields`**: Space-separated `name:type` output fields (`string`, `integer`, `number` or `boolean`). If omitted, an `example_field` is generated. Use the full [structured output DSL](agent/structured-output.md) in the generated file for enums, nested objects or optional fields.
65
+
66
+ ## Eval Generator
67
+
68
+ ```bash
69
+ rails generate omni_agent:eval ResearchAgent
70
+ ```
71
+
72
+ Generates `evals/research_agent_eval.rb` with a starter `eval_case` for `ResearchAgent`. See [Generating and Running Evals](evals/generating-and-running.md), [Golden Sets](evals/golden-sets.md), [Judge Provider](evals/judge-provider.md), and [Caching](evals/caching.md).
73
+
74
+ ## MCP Server Generator
75
+
76
+ ```bash
77
+ rails generate omni_agent:mcp_server Support --with-tools LookupOrder RefundOrder --agents SupportAgent
78
+ ```
79
+
80
+ Generates:
81
+
82
+ ```text
83
+ app/mcp_servers/
84
+ ├── support_server.rb
85
+ └── support_server/
86
+ └── tools/
87
+ ├── lookup_order.rb
88
+ └── refund_order.rb
89
+ ```
90
+
91
+ The server is protected by bearer tokens from `SUPPORT_MCP_TOKENS`. The generator also adds this route:
92
+
93
+ ```ruby
94
+ mcp_server :support
95
+ ```
96
+
97
+ If `config/routes.rb` already mounts every server with a bare `mcp_servers`, the generator skips the route because the new server is mounted automatically.
98
+
99
+ It then prints the commands to connect Claude Code over HTTP and stdio.
100
+
101
+ ### Options
102
+
103
+ * **`--with-tools`**: Space-separated list of server tools to scaffold under `app/mcp_servers/<name>_server/tools/`. They are added to the server automatically. If you pass `--with-tools` with no names, an `ExampleTool` is generated.
104
+ * **`--tools`**: Existing tool classes (for example, agent tools) to expose with `tools`.
105
+ * **`--agents`**: Agents whose tools are all exposed with `tools_from`.
106
+ * **`--path`**: Mount path, written as `mcp_server :support, path: "..."`. Defaults to `/mcp/<name>`.
107
+
108
+ The `Server` suffix is added if you leave it out (`Support` → `SupportServer`). See [Defining MCP Servers](mcp/defining-servers.md).
109
+
110
+ ## Embeddings Generator
111
+
112
+ ```bash
113
+ rails generate omni_agent:embeddings Document title content
114
+ ```
115
+
116
+ Adds an embedding column to the `Document` model and declares `has_embedding :title, :content` on it, so the model can be searched by agents with [`searches`](rag.md#searches). The migration matches your database: a pgvector `vector` column with a cosine index on Postgres, a `binary` column plus the sqlite-vec initializer on SQLite, a `vector` column on MariaDB. The vector size is detected by embedding one string with your configured model.
117
+
118
+ It then prints the commands to install the gems, migrate and backfill existing records.
119
+
120
+ ### Options
121
+
122
+ * **`--backend`**: `pgvector`, `sqlite`, `mariadb` or `ruby`. Defaults to the one matching your database adapter. Plain MySQL is refused unless you pass `--backend=ruby`, which is [for small tables only](rag.md#ruby-fallback-small-tables-only).
123
+ * **`--dimensions`**: Vector size. Pass it when the embedding model can't be reached (offline, no API key).
124
+ * **`--column`**: Vector column name. Defaults to `embedding`.
125
+ * **`--later`**: Embed in a [background job](rag.md#background-embedding) after commit instead of during `save`.
126
+
127
+ ## Chunks Generator
128
+
129
+ ```bash
130
+ rails generate omni_agent:chunks Document file
131
+ ```
132
+
133
+ Creates a `DocumentChunk` model and its table (with a vector column that matches your database, like the [embeddings generator](#embeddings-generator)) and declares `has_chunks :file` on `Document`, so its [documents are split and searched in chunks](rag.md#documents). If `file` is neither declared on the model nor a column, it's taken as an upload and `has_one_attached :file` is added too.
134
+
135
+ It then prints the commands to install the gems, migrate and ingest existing records.
136
+
137
+ ### Options
138
+
139
+ * **`--as`**: A [loader](rag.md#loaders) to enforce (`pdf`, `html`, `markdown`, `text`). Defaults to detecting each file's format.
140
+ * **`--chunker`**: `recursive`, `markdown` or `hierarchical`. Defaults to `recursive`.
141
+ * **`--size`** / **`--overlap`**: Chunk size and overlap, in characters.
142
+ * **`--inline`**: Ingest right after commit instead of in a background job.
143
+ * **`--backend`** / **`--dimensions`**: Same as the [embeddings generator](#options-3).
data/docs/index.md ADDED
@@ -0,0 +1,98 @@
1
+ # OmniAgent Documentation
2
+
3
+ Offline copy of https://omniagent.activetaskers.com/docs/intro, bundled with the `omni_agent` gem so coding
4
+ agents (Claude Code, Cursor, Codex...) can learn how the gem works from inside
5
+ a project that depends on it. Every page is plain Markdown; links between
6
+ pages are relative.
7
+
8
+ Locate these files from a host app with:
9
+
10
+ ```bash
11
+ bundle exec omni_agent docs # print this index
12
+ bundle exec omni_agent docs tools # print one page (path without .md)
13
+ bundle exec omni_agent docs --path # print the directory holding these files
14
+ ```
15
+
16
+ ## Conventions at a Glance
17
+
18
+ OmniAgent is convention over configuration: classes are discovered from
19
+ where they live, so put files where the generators put them.
20
+
21
+ | What | Where | Class |
22
+ | --- | --- | --- |
23
+ | Global settings | `config/initializers/omni_agent.rb` (written by `omni_agent:install`) | `OmniAgent.configure` |
24
+ | Agent | `app/agents/<name>.rb` | `<Name> < OmniAgent::Agent` |
25
+ | Agent system prompt | `app/agents/<name>/prompt.md.erb` | |
26
+ | Entrypoint prompt | `app/agents/<name>/<entrypoint>.md.erb` | |
27
+ | Agent tool | `app/agents/<name>/tools/<tool>.rb` | `<Name>::Tools::<Tool> < OmniAgent::Tool` |
28
+ | MCP server | `app/mcp_servers/<name>_server.rb` | `<Name>Server < OmniAgent::MCP::Server` |
29
+ | MCP server tool | `app/mcp_servers/<name>_server/tools/<tool>.rb` | `<Name>Server::Tools::<Tool>` |
30
+ | Eval | `evals/<name>_eval.rb` | `< OmniAgent::Eval` |
31
+
32
+ Prefer the generators (`rails generate omni_agent:agent`, `omni_agent:mcp_server`,
33
+ `omni_agent:stock_agent`, `omni_agent:eval`, `omni_agent:embeddings`,
34
+ `omni_agent:chunks`) over writing these files by hand.
35
+ See [Generators](generators.md).
36
+
37
+ ## Typical Flows
38
+
39
+ * **Build an agent:** [Introduction](intro.md) → [Tools](tools.md) →
40
+ [Run Entrypoints](agent/run-entrypoints.md) → [Structured Output](agent/structured-output.md).
41
+ * **Pick a provider or model:** [Supported Providers](supported-providers.md),
42
+ [Configuration](configuration.md), [Change an Agent's Provider or Model](agent/provider.md).
43
+ * **Compose agents:** [Multi-Agent Delegation](agent/delegation.md),
44
+ [Stock Agents](stock-agents/overview.md).
45
+ * **Observe a run:** [Generation Callbacks](agent/callbacks.md),
46
+ [Streaming Responses](agent/streaming.md).
47
+ * **Answer from your own data (RAG):** [Retrieval-Augmented Generation](rag.md)
48
+ (`has_embedding`, `has_chunks` and the `searches` agent macro).
49
+ * **Test quality:** [Generating and Running Evals](evals/generating-and-running.md) →
50
+ [Golden Sets](evals/golden-sets.md) → [Judge Provider](evals/judge-provider.md).
51
+ * **Expose tools to MCP clients:** [Defining MCP Servers](mcp/defining-servers.md) →
52
+ [Authentication](mcp/authentication.md) → [Transports and Clients](mcp/transports.md).
53
+
54
+ ## All Pages
55
+
56
+ ### Core
57
+
58
+ * [Introduction](intro.md): **Omni Agent** is a gem focused on generating agentic code the Rails way.
59
+ * [Supported providers](supported-providers.md): This is a list of our currently supported providers.
60
+ * [Configuration](configuration.md): Omni Agent ships with sensible defaults but lets you configure global behavior through an initializer.
61
+ * [Tools](tools.md): Tools are how agents take action beyond generating text. Every tool is a small class that declares an input schema and implements `#execute`.
62
+ * [Generators](generators.md): Omni Agent ships seven Rails generators to scaffold your project.
63
+ * [Retrieval-Augmented Generation (RAG)](rag.md): RAG lets an agent answer from your own data: it searches your records for the passages relevant to a question and gives them to the model. Omni Agent keeps the vectors in your own database (or Meilisearch) and turns any searchable model into an agent tool. Records are searched whole with `has_embedding`, and files or long text in chunks with `has_chunks`.
64
+
65
+ ### Agents
66
+
67
+ Learn about agent specific features and functionalities.
68
+
69
+ * [Generation Callbacks](agent/callbacks.md): Agents support `before_generation` and `after_generation` callbacks, letting you hook into the lifecycle of a single `run` without overriding it yourself.
70
+ * [Run Entrypoints](agent/run-entrypoints.md): `OmniAgent::Agent` always exposes the low-level `run` method, but you'll usually call an agent through one of its higher-level entrypoints.
71
+ * [Change an Agent's Provider or Model](agent/provider.md): By default, agents use the configured `default_provider` and `default_model`. You can override these settings on a per-agent basis using one of the following DSL directives.
72
+ * [Multi-Agent Delegation](agent/delegation.md): `delegate_to` wraps another agent as a tool your agent's LLM can call, turning your agent into a **supervisor** that routes work to sub-agents. The delegated agent is defined and used like any other agent — a normal class under `app/agents/`, not a manual tool file.
73
+ * [Streaming Responses](agent/streaming.md): Any run entrypoint — `run`, an implicit entrypoint, or a `run_aliases` method — can stream its response by prefixing the call with `.stream` and passing a block. Without `.stream` (or without a block), behavior is completely unchanged.
74
+ * [Structured Output](agent/structured-output.md): Declare an `output` block to make an agent return a validated object instead of free text. It uses the same DSL as a tool's `input`.
75
+
76
+ ### Stock Agents
77
+
78
+ Ready-made agents shipped with the gem that your project subclasses and customizes.
79
+
80
+ * [Overview](stock-agents/overview.md): Stock agents are ready-made agents shipped with the gem under `OmniAgent::StockAgents`. They bring their own prompt, model settings and tools; your project subclasses one and declares the structured output it needs.
81
+ * [OCR](stock-agents/ocr.md): `OmniAgent::StockAgents::OCR` reads photos, scans and PDFs with a vision model (`gpt-4o` by default). Its default output is `{ text: "..." }`, the full text in reading order; declare your own `output` to extract fields instead.
82
+
83
+ ### Evals
84
+
85
+ Test agent quality with deterministic assertions and LLM-as-judge scoring.
86
+
87
+ * [Generating and Running Evals](evals/generating-and-running.md): `OmniAgent::Eval` lets you test agent *quality*, not just correctness — deterministic assertions (tool calls, output matching) plus pluggable LLM-as-judge scoring.
88
+ * [Golden Sets](evals/golden-sets.md): For many input/expected-output pairs, load a dataset file instead of writing a `eval_case` per row.
89
+ * [Judge Provider](evals/judge-provider.md): `judge` resolves which provider grades the output, in this order.
90
+ * [Caching](evals/caching.md): Eval runs are cached by default, keyed on `(agent class, run_alias, input, context)`, and stored as JSON at `OmniAgent.configuration.eval_cache_path` (default `tmp/omni_agent_eval_cache.json`). Re-running the same case — e.g. while iterating on assertions — replays the cached output instead of calling the provider again, so you don't spend tokens testing the same case twice.
91
+
92
+ ### MCP Servers
93
+
94
+ Expose your tools and agents to Claude Code, Claude Desktop, Cursor and other MCP clients.
95
+
96
+ * [Defining MCP Servers](mcp/defining-servers.md): The Model Context Protocol (MCP) lets AI clients such as Claude Code, Claude Desktop and Cursor call tools that live in your app. `OmniAgent::MCP::Server` turns the `OmniAgent::Tool` classes you already wrote for your agents — and the agents themselves — into an MCP server, with no extra gem.
97
+ * [Authentication](mcp/authentication.md): An MCP server mounted over HTTP lets anyone who can reach the URL run your tools. For that reason, **every server must declare `authenticate`**. A server without it raises `OmniAgent::MCPError` on the first HTTP request. To make a server public on purpose, write `authenticate :none`.
98
+ * [Transports and Clients](mcp/transports.md): OmniAgent supports both MCP transports.
data/docs/intro.md ADDED
@@ -0,0 +1,93 @@
1
+ <!-- Generated from omniagent-docs/docs/intro.mdx by `bundle exec rake docs:sync`. Do not edit by hand. -->
2
+ <!-- Online version: https://omniagent.activetaskers.com/docs/intro -->
3
+
4
+ # Introduction
5
+
6
+ **Omni Agent** is a gem focused on generating agentic code the Rails way.
7
+
8
+ It is designed to be highly opinionated, fully embracing the "convention over configuration" philosophy.
9
+
10
+ Let's get your first AI agent up and running **in less than 5 minutes**.
11
+
12
+ ## Getting Started
13
+
14
+ ### 1. Install the Gem
15
+ First, add Omni Agent to your Rails application. You can add it directly to your `Gemfile`:
16
+
17
+ ```ruby
18
+ gem 'omni_agent'
19
+ ```
20
+
21
+ Or, run the following command in your terminal:
22
+
23
+ ```bash
24
+ bundle add omni_agent
25
+ ```
26
+
27
+ ### 2. Install an AI Provider
28
+
29
+ Omni Agent is provider-agnostic, meaning you need to install the specific gem for the AI provider you plan to use. You can find a full list of supported providers [here](supported-providers.md).
30
+
31
+ For example, to use OpenAI:
32
+
33
+ ```bash
34
+ bundle add openai
35
+ ```
36
+
37
+ ### 3. Run the Install Generator
38
+
39
+ Prepare your project by running the Omni Agent installation generator:
40
+
41
+ ```bash
42
+ rails generate omni_agent:install
43
+ ```
44
+
45
+ This will set up the necessary foundation for your agents, starting by creating the core `app/agents` directory.
46
+
47
+ > **Note:** As of version `0.1.4`, this command is lightweight. It lays the groundwork for more advanced configurations and planned features in future releases.
48
+
49
+ ## Create Your First Agent
50
+
51
+ With the setup complete, you can use the built-in generator to scaffold your first agent.
52
+
53
+ Run the following command to create a `DemoAgent`, specify the model, and attach a couple of tools:
54
+
55
+ ```bash
56
+ rails generate omni_agent:agent DemoAgent --model gpt-4o-mini --with-tools DemoTool SecondDemoTool
57
+ ```
58
+
59
+ This will generate the following directory structure inside `app/agents/`:
60
+
61
+ ```text
62
+ app/agents/
63
+ ├── demo_agent.rb
64
+ └── demo_agent/
65
+ ├── prompt.md.erb
66
+ └── tools/
67
+ ├── demo_tool.rb
68
+ └── second_demo_tool.rb
69
+
70
+ ```
71
+
72
+ **What are these files?**
73
+
74
+ * **`demo_agent.rb`**: The main configuration file dictating how your agent behaves.
75
+ * **`prompt.md.erb`**: The core system prompt injected into your agent. It uses ERB, allowing for dynamic Ruby interpolation.
76
+ * **`tools/`**: The directory housing the custom tools (`DemoTool`, `SecondDemoTool`) at your agent's disposal.
77
+
78
+ ## Calling the Agent
79
+
80
+ After running the generator, your agent is ready to go out of the box. You can quickly test it in your Rails console (`rails c`) or anywhere in your app:
81
+
82
+ ```ruby
83
+ DemoAgent.new.run("Hello!")
84
+ ```
85
+
86
+ Congratulations - you are successfully using Omni Agent!
87
+
88
+ ## What Else Can It Do?
89
+
90
+ * **[Tools](tools.md)** with typed, validated input schemas.
91
+ * **[Multi-agent delegation](agent/delegation.md)**, **[streaming](agent/streaming.md)** and **[callbacks](agent/callbacks.md)**.
92
+ * **[Evals](index.md#evals)** with golden sets and LLM-as-judge scoring.
93
+ * **[MCP servers](index.md#mcp-servers)**: expose your tools and agents to Claude Code, Claude Desktop, Cursor and other MCP clients.