laiya 0.0.2

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 (44) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/bin/laiya +20 -0
  4. data/context/chatgpt-codex.md +43 -0
  5. data/context/getting-started.md +49 -0
  6. data/context/http-api.md +49 -0
  7. data/context/index.yaml +26 -0
  8. data/context/providers-and-models.md +104 -0
  9. data/examples/chatgpt/authentication.rb +45 -0
  10. data/examples/chatgpt/readme.md +107 -0
  11. data/examples/chatgpt/service.rb +34 -0
  12. data/guides/chatgpt-codex/readme.md +43 -0
  13. data/guides/getting-started/readme.md +49 -0
  14. data/guides/http-api/readme.md +49 -0
  15. data/guides/index.md +14 -0
  16. data/guides/links.yaml +8 -0
  17. data/guides/providers-and-models/readme.md +104 -0
  18. data/lib/laiya/configuration/builder.rb +40 -0
  19. data/lib/laiya/configuration.rb +152 -0
  20. data/lib/laiya/environment/application.rb +53 -0
  21. data/lib/laiya/models/catalog.rb +108 -0
  22. data/lib/laiya/models/discover.rb +110 -0
  23. data/lib/laiya/provider/codex/authentication.rb +185 -0
  24. data/lib/laiya/provider/codex/request.rb +136 -0
  25. data/lib/laiya/provider/codex/response.rb +117 -0
  26. data/lib/laiya/provider/codex/responses.rb +93 -0
  27. data/lib/laiya/provider/codex/server_sent_events.rb +78 -0
  28. data/lib/laiya/provider/codex.rb +298 -0
  29. data/lib/laiya/provider/interface.rb +19 -0
  30. data/lib/laiya/provider/ollama.rb +22 -0
  31. data/lib/laiya/provider/openai.rb +92 -0
  32. data/lib/laiya/provider.rb +10 -0
  33. data/lib/laiya/router.rb +85 -0
  34. data/lib/laiya/service/application.rb +50 -0
  35. data/lib/laiya/version.rb +8 -0
  36. data/lib/laiya/web/application.rb +48 -0
  37. data/lib/laiya/web.rb +6 -0
  38. data/lib/laiya.rb +19 -0
  39. data/license.md +21 -0
  40. data/readme.md +68 -0
  41. data/releases.md +7 -0
  42. data.tar.gz.sig +0 -0
  43. metadata +162 -0
  44. metadata.gz.sig +3 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 665e132b315d14e0d867411d06e49c015ed8bfe18f50e3ee15623eee73a90e6e
4
+ data.tar.gz: 693d030853123c19d1dda07fda06fac7d6c6b8a8d4b9db7a488cbda3e8ef2add
5
+ SHA512:
6
+ metadata.gz: 6bb1a3cc19e46eaf6908343327edeb7c4af1867b07016962b4a6a70c22d7f31e44aa3bdf644f6a26ad2ed4d152dfe041f3d416a912aea00005076f988f60c198
7
+ data.tar.gz: '015801fda685fe3fc0e3fe1d792784fdd4aaa6255e04d6e1d0ea29dc72b5c4360d513834e5681d4ca3b4092067231548e43bf87773b0066930ab4677bdaeac34'
checksums.yaml.gz.sig ADDED
Binary file
data/bin/laiya ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Released under the MIT License.
5
+ # Copyright, 2026, by Samuel Williams.
6
+
7
+ require "async/service"
8
+ require_relative "../lib/laiya/environment/application"
9
+
10
+ ARGV.each do |path|
11
+ require(path)
12
+ end
13
+
14
+ configuration = Async::Service::Configuration.build do
15
+ service "laiya" do
16
+ include Laiya::Environment::Application
17
+ end
18
+ end
19
+
20
+ Async::Service::Controller.run(configuration)
@@ -0,0 +1,43 @@
1
+ # ChatGPT Codex Provider
2
+
3
+ This guide explains how to run the experimental Codex provider with a ChatGPT login while keeping tool execution on the client.
4
+
5
+ ## Authentication and Trust
6
+
7
+ The Codex provider reads ChatGPT credentials from
8
+ `CODEX_HOME/auth.json` (default `~/.codex/auth.json`) and refreshes tokens when
9
+ needed. If the Codex CLI uses an OS keyring, configure file-backed credentials
10
+ and run `codex login`:
11
+
12
+ ```toml
13
+ cli_auth_credentials_store = "file"
14
+ ```
15
+
16
+ Treat `auth.json` as a password. This provider calls the Codex-specific backend,
17
+ not the public OpenAI Platform API. It is experimental and intended for a
18
+ trusted, single-user service. Do not expose it to the public internet or an
19
+ untrusted multi-user environment.
20
+
21
+ ## Start the Example
22
+
23
+ From the Laiya repository root:
24
+
25
+ ```sh
26
+ export LAIYA_API_KEY="$(openssl rand -hex 32)"
27
+ bundle exec ruby examples/chatgpt/service.rb
28
+ ```
29
+
30
+ The example binds to `127.0.0.1:9293`. It requires the client to send the
31
+ separate `LAIYA_API_KEY` as a Bearer token. For remote clients, use TLS and a
32
+ private access-controlled network. See the [example files](https://github.com/socketry/laiya/tree/main/examples/chatgpt).
33
+
34
+ ## Client-Owned Tool Execution
35
+
36
+ Use `POST /v1/responses` for tool calling. Laiya preserves the Codex Responses
37
+ stream and response items. The client executes a returned function call and
38
+ sends its `function_call_output` back in the next request. Since Codex is used
39
+ statelessly, send the full input history, including the previous response's
40
+ reasoning and function-call items; `previous_response_id` is not supported.
41
+
42
+ The text-only `POST /v1/chat/completions` adapter rejects tool-enabled requests
43
+ so it cannot silently lose Codex reasoning state.
@@ -0,0 +1,49 @@
1
+ # Getting Started
2
+
3
+ This guide explains how to install Laiya, configure a provider, and make an OpenAI-compatible request.
4
+
5
+ ## Installation
6
+
7
+ Add Laiya to your application:
8
+
9
+ ```sh
10
+ bundle add laiya
11
+ ```
12
+
13
+ ## Configure a Provider
14
+
15
+ Laiya's Ruby API uses a `Laiya::Configuration` to connect model IDs to
16
+ providers. For an OpenAI Platform API key:
17
+
18
+ ```ruby
19
+ require "laiya"
20
+
21
+ configuration = Laiya::Configuration.build do |builder|
22
+ builder.provider :openai, Laiya::Provider::OpenAI.new(
23
+ api_key: ENV.fetch("OPENAI_API_KEY")
24
+ )
25
+ builder.model "gpt-4.1-mini", provider: :openai
26
+ builder.default_provider :openai
27
+ end
28
+
29
+ application = Laiya::Web::Application.new(configuration: configuration)
30
+ ```
31
+
32
+ The provider forwards `Protocol::HTTP::Request` and `Protocol::HTTP::Response`
33
+ objects using `Async::HTTP`. It does not parse OpenAI request or response bodies
34
+ in the direct proxy path.
35
+
36
+ For Ollama and multi-provider setups, see [Providers and Models](../providers-and-models/).
37
+
38
+ ## Run the API Service
39
+
40
+ For a local OpenAI API proxy, set the API key and start Laiya:
41
+
42
+ ```sh
43
+ export OPENAI_API_KEY="..."
44
+ bundle exec bin/laiya
45
+ ```
46
+
47
+ The service listens on `http://localhost:9292` by default. Configure `LAIYA_URL`
48
+ to change the endpoint. See [HTTP API](../http-api/) for supported routes and
49
+ streaming behavior.
@@ -0,0 +1,49 @@
1
+ # HTTP API
2
+
3
+ This guide explains how Laiya's OpenAI-compatible HTTP API handles routing, proxying, and streaming.
4
+
5
+ ## Run the Async Service
6
+
7
+ Laiya runs directly on `Async::HTTP::Server` under `Async::Service`; it does not
8
+ use Rack:
9
+
10
+ ```sh
11
+ bundle exec bin/laiya
12
+ ```
13
+
14
+ The default service listens on `http://localhost:9292`. `LAIYA_URL` configures
15
+ the endpoint. `bin/laiya` loads optional Ruby configuration paths supplied on
16
+ the command line before starting the service.
17
+
18
+ ## Endpoints
19
+
20
+ The API accepts OpenAI-compatible `Protocol::HTTP::Request` objects and returns
21
+ `Protocol::HTTP::Response` objects. Supported common routes include:
22
+
23
+ - `GET /v1/models`
24
+ - `POST /v1/chat/completions`
25
+ - `POST /v1/responses` when the configured provider supports it
26
+ - Other OpenAI-compatible paths are forwarded to the selected provider.
27
+
28
+ Configured model routes produce a Laiya model catalog. When a provider is the
29
+ default and no model catalog is configured, `GET /v1/models` is forwarded
30
+ upstream. See [Providers and Models](../providers-and-models/) for discovery.
31
+
32
+ ## Proxy and Model Routing
33
+
34
+ The built-in OpenAI provider is a direct HTTP proxy. It forwards the method,
35
+ path, headers, and request body, replacing authorization with its configured
36
+ upstream key and filtering hop-by-hop headers. It returns the upstream response
37
+ without parsing its body.
38
+
39
+ When a model route or discovery source is configured, Laiya buffers and rewinds
40
+ request bodies for known JSON completion endpoints to read the `model` field.
41
+ It forwards the original body unchanged. Streaming response bodies are not
42
+ buffered by the router.
43
+
44
+ ## Streaming
45
+
46
+ OpenAI-compatible upstream SSE responses pass through unchanged. Translating
47
+ providers may adapt streaming formats. The Codex Responses endpoint passes
48
+ native Responses SSE through, including tool-call events; Laiya does not execute
49
+ client tools.
@@ -0,0 +1,26 @@
1
+ # Automatically generated context index for Utopia::Project guides.
2
+ # Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
3
+ ---
4
+ description: An OpenAI-compatible HTTP API and provider proxy
5
+ metadata:
6
+ bug_tracker_uri: https://github.com/socketry/laiya/issues
7
+ changelog_uri: https://github.com/socketry/laiya/blob/main/releases.md
8
+ documentation_uri: https://socketry.github.io/laiya/
9
+ source_code_uri: https://github.com/socketry/laiya.git
10
+ files:
11
+ - path: getting-started.md
12
+ title: Getting Started
13
+ description: This guide explains how to install Laiya, configure a provider, and
14
+ make an OpenAI-compatible request.
15
+ - path: providers-and-models.md
16
+ title: Providers and Models
17
+ description: This guide explains how to route model IDs to providers and when to
18
+ discover a provider's model catalog.
19
+ - path: http-api.md
20
+ title: HTTP API
21
+ description: This guide explains how Laiya's OpenAI-compatible HTTP API handles
22
+ routing, proxying, and streaming.
23
+ - path: chatgpt-codex.md
24
+ title: ChatGPT Codex Provider
25
+ description: This guide explains how to run the experimental Codex provider with
26
+ a ChatGPT login while keeping tool execution on the client.
@@ -0,0 +1,104 @@
1
+ # Providers and Models
2
+
3
+ This guide explains how to route model IDs to providers and when to discover a provider's model catalog.
4
+
5
+ ## Default Provider
6
+
7
+ Use a default provider when one upstream should receive any model name:
8
+
9
+ ```ruby
10
+ configuration = Laiya::Configuration.build do |builder|
11
+ builder.provider :ollama, Laiya::Provider::Ollama.new
12
+ builder.default_provider :ollama
13
+ end
14
+ ```
15
+
16
+ With no explicit model routes or discovery setting, Laiya forwards
17
+ `GET /v1/models` to the default provider and passes completion requests through
18
+ to it.
19
+
20
+ ## Discover Models from an Upstream
21
+
22
+ Providers such as Ollama expose an OpenAI-compatible `GET /v1/models` endpoint.
23
+ Use `models: :discover` to query it at runtime instead of copying model names
24
+ into configuration:
25
+
26
+ ```ruby
27
+ configuration = Laiya::Configuration.build do |builder|
28
+ builder.provider :ollama, Laiya::Provider::Ollama.new, models: :discover
29
+ builder.default_provider :ollama
30
+ end
31
+ ```
32
+
33
+ `Laiya::Models::Discover` fetches and caches the provider's catalog for 60
34
+ seconds. Laiya uses the discovered IDs for `GET /v1/models` and routes requests
35
+ for those IDs to the provider. The builder itself does not make network calls.
36
+
37
+ For multiple providers, enable discovery on each provider. Distinct model IDs
38
+ are routed automatically. If two providers advertise the same ID, the default
39
+ provider wins; an explicit `builder.model` route overrides discovery:
40
+
41
+ ```ruby
42
+ configuration = Laiya::Configuration.build do |builder|
43
+ builder.provider :openai, Laiya::Provider::OpenAI.new(
44
+ api_key: ENV.fetch("OPENAI_API_KEY")
45
+ ), models: :discover
46
+ builder.provider :ollama, Laiya::Provider::Ollama.new, models: :discover
47
+
48
+ builder.model "llama3.2", provider: :ollama
49
+ builder.default_provider :openai
50
+ end
51
+ ```
52
+
53
+ ## Expose Model Limits
54
+
55
+ OpenAI's standard model-list format has no context-window fields. Laiya lets
56
+ you attach a display name and limits to an explicit model route; it publishes
57
+ them under its `laiya` extension field:
58
+
59
+ ```ruby
60
+ configuration = Laiya::Configuration.build do |builder|
61
+ builder.provider :ollama, Laiya::Provider::Ollama.new, models: :discover
62
+ builder.model "llama3.2",
63
+ provider: :ollama,
64
+ display_name: "Llama 3.2",
65
+ limits: {context: 32_768, input: 28_672, output: 4_096}
66
+ builder.default_provider :ollama
67
+ end
68
+ ```
69
+
70
+ The `limits` keys are `context`, `input`, and `output`, and values are positive
71
+ token counts. OpenCode does not currently infer these custom fields from
72
+ `GET /v1/models`; configure its model `limit.context`, `limit.input`, and
73
+ `limit.output` values on each client as well. The `context` limit should match
74
+ the effective upstream model configuration (for Ollama, including `num_ctx`).
75
+
76
+ ## Customize Discovery
77
+
78
+ Subclass `Laiya::Models::Discover` to filter models or attach provider-specific
79
+ metadata. Override `include_model?` to filter entries and `normalize_model` to
80
+ add or adjust model fields:
81
+
82
+ ```ruby
83
+ class LocalModels < Laiya::Models::Discover
84
+ protected
85
+
86
+ def include_model?(model)
87
+ super && model["id"].start_with?("llama")
88
+ end
89
+
90
+ def normalize_model(model)
91
+ super.merge("laiya" => {"limits" => {"context" => 32_768, "output" => 4_096}})
92
+ end
93
+ end
94
+
95
+ ollama = Laiya::Provider::Ollama.new
96
+ configuration = Laiya::Configuration.build do |builder|
97
+ builder.provider :ollama, ollama, models: LocalModels.new(ollama)
98
+ builder.default_provider :ollama
99
+ end
100
+ ```
101
+
102
+ The model list includes configured IDs and discovered metadata. Custom discovery
103
+ metadata is provider-specific and is likewise exposed under Laiya's extension
104
+ field; standard clients may ignore it.
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2026, by Samuel Williams.
5
+
6
+ require "json"
7
+ require "openssl"
8
+ require "protocol/http/middleware"
9
+
10
+ module Examples
11
+ module ChatGPT
12
+ # Requires a local API key before forwarding to the personal Codex account.
13
+ class Authentication < Protocol::HTTP::Middleware
14
+ def initialize(delegate, token:)
15
+ super(delegate)
16
+ raise ArgumentError, "LAIYA_API_KEY must not be empty" if token.empty?
17
+
18
+ @token = token
19
+ end
20
+
21
+ def call(request)
22
+ scheme, supplied = request.headers["authorization"].to_s.split(" ", 2)
23
+
24
+ unless valid_token?(scheme, supplied)
25
+ return Protocol::HTTP::Response[
26
+ 401,
27
+ {"content-type" => "application/json", "www-authenticate" => "Bearer"},
28
+ [JSON.dump(error: {message: "Invalid API key", type: "authentication_error"})],
29
+ ]
30
+ end
31
+
32
+ return @delegate.call(request)
33
+ end
34
+
35
+ private
36
+
37
+ def valid_token?(scheme, supplied)
38
+ return false unless scheme == "Bearer" && supplied
39
+ return false unless supplied.bytesize == @token.bytesize
40
+
41
+ OpenSSL.fixed_length_secure_compare(supplied, @token)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,107 @@
1
+ # Laiya with ChatGPT Codex
2
+
3
+ This example uses `Laiya::Provider::Codex` to adapt Laiya's OpenAI Chat
4
+ Completions API to OpenAI's Codex Responses backend. It loads the ChatGPT login
5
+ from the Codex CLI's file-based credentials, refreshes credentials as needed,
6
+ and converts Responses events—including function calls—back to Chat Completions.
7
+ Tools are returned to the API caller; Laiya does not execute them.
8
+
9
+ This is an experimental, single-account integration with the Codex-specific
10
+ backend, not the public OpenAI Platform API. Backend behavior and supported
11
+ models may change. OpenAI documents ChatGPT authentication for local Codex
12
+ workflows and trusted private automation; do not run this as a public or
13
+ multi-user service.
14
+
15
+ ## Prepare credentials
16
+
17
+ The Codex provider reads `CODEX_HOME/auth.json` (default `~/.codex/auth.json`).
18
+ It requires ChatGPT login credentials with access and refresh tokens. If the
19
+ Codex CLI currently stores credentials in the OS keyring, configure file-based
20
+ storage in `~/.codex/config.toml`:
21
+
22
+ ```toml
23
+ cli_auth_credentials_store = "file"
24
+ ```
25
+
26
+ Then run `codex login` and verify `codex login status`. Laiya never prints or
27
+ logs the credential values. Treat `auth.json` as a password; on a remote host,
28
+ transfer it only through a secret manager or another trusted secure channel and
29
+ restrict its permissions to the service account.
30
+
31
+ ## Run locally
32
+
33
+ Create a client key and start the service from the Laiya repository root:
34
+
35
+ ```sh
36
+ export LAIYA_API_KEY="$(openssl rand -hex 32)"
37
+ bundle install
38
+ bundle exec ruby examples/chatgpt/service.rb
39
+ ```
40
+
41
+ By default it binds only to `127.0.0.1:9293`. Keep it local unless you put it
42
+ behind TLS and an access-controlled gateway. The service requires the caller to
43
+ send the `LAIYA_API_KEY` as a Bearer token; this is separate from the Codex
44
+ credentials used upstream.
45
+
46
+ `LAIYA_CHATGPT_MODEL` selects the model exposed by `GET /v1/models` and sent to
47
+ the Codex backend. It defaults to `gpt-6-luna`; set it to a model available to
48
+ your account. Set `CODEX_HOME` if the credential file is in a non-default
49
+ directory. `LAIYA_URL` can change the bind endpoint; do not bind publicly
50
+ without TLS and network access controls.
51
+
52
+ ## Try it
53
+
54
+ ```sh
55
+ curl http://127.0.0.1:9293/v1/chat/completions \
56
+ -H "authorization: Bearer $LAIYA_API_KEY" \
57
+ -H 'content-type: application/json' \
58
+ -d '{"model":"gpt-6-luna","messages":[{"role":"user","content":"Say hello"}]}'
59
+ ```
60
+
61
+ For the native Responses endpoint, send `input` as message items (this is also
62
+ the format used when preserving tool-call history):
63
+
64
+ ```sh
65
+ curl -sS http://127.0.0.1:9293/v1/responses \
66
+ -H "authorization: Bearer $LAIYA_API_KEY" \
67
+ -H 'content-type: application/json' \
68
+ -d '{"model":"gpt-6-luna","input":[{"role":"user","content":[{"type":"input_text","text":"Reply with exactly: Laiya connected"}]}]}'
69
+ ```
70
+
71
+ Use `/v1/responses` for Codex tool calling. Laiya preserves the Responses API
72
+ tool-call items and streaming events, so the client executes the tools and
73
+ sends `function_call_output` items in the next request. The text-only
74
+ `/v1/chat/completions` adapter rejects tool-enabled requests because converting
75
+ away Codex's encrypted reasoning state would break some tool continuations.
76
+ Send full Responses input history on each request; `previous_response_id` is not
77
+ available because the Codex backend is used statelessly.
78
+
79
+ ## Connect OpenCode from another computer
80
+
81
+ Put the service behind TLS and an access-controlled network gateway, then add a
82
+ custom Responses-capable OpenAI provider to OpenCode. Keep the Laiya API key in
83
+ the client machine's secret configuration rather than committing it:
84
+
85
+ ```json
86
+ {
87
+ "$schema": "https://opencode.ai/config.json",
88
+ "provider": {
89
+ "laiya": {
90
+ "npm": "@ai-sdk/openai",
91
+ "name": "Laiya Codex",
92
+ "options": {
93
+ "baseURL": "https://laiya.example.com/v1",
94
+ "apiKey": "<LAIYA_API_KEY>"
95
+ },
96
+ "models": {
97
+ "gpt-6-luna": {"name": "GPT-6 Luna"}
98
+ }
99
+ }
100
+ },
101
+ "model": "laiya/gpt-6-luna"
102
+ }
103
+ ```
104
+
105
+ OpenCode uses `/v1/responses` for this provider. The model can return tool calls,
106
+ which OpenCode executes on the client computer and reports back on subsequent
107
+ requests; Laiya never executes client tools.
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Released under the MIT License.
5
+ # Copyright, 2026, by Samuel Williams.
6
+
7
+ require "async/http"
8
+ require "async/service"
9
+ require "async/service/managed/environment"
10
+ require "laiya"
11
+ require "laiya/environment/application"
12
+ require_relative "authentication"
13
+
14
+ configuration = Async::Service::Configuration.build do
15
+ service "laiya-chatgpt" do
16
+ include Laiya::Environment::Application
17
+
18
+ configuration do
19
+ Laiya::Configuration.build do |builder|
20
+ builder.provider :codex, Laiya::Provider::Codex.new
21
+ builder.model ENV.fetch("LAIYA_CHATGPT_MODEL", "gpt-6-luna"), provider: :codex
22
+ builder.default_provider :codex
23
+ end
24
+ end
25
+
26
+ application do
27
+ Examples::ChatGPT::Authentication.new(super(),
28
+ token: ENV.fetch("LAIYA_API_KEY"),
29
+ )
30
+ end
31
+ end
32
+ end
33
+
34
+ Async::Service::Controller.run(configuration)
@@ -0,0 +1,43 @@
1
+ # ChatGPT Codex Provider
2
+
3
+ This guide explains how to run the experimental Codex provider with a ChatGPT login while keeping tool execution on the client.
4
+
5
+ ## Authentication and Trust
6
+
7
+ The Codex provider reads ChatGPT credentials from
8
+ `CODEX_HOME/auth.json` (default `~/.codex/auth.json`) and refreshes tokens when
9
+ needed. If the Codex CLI uses an OS keyring, configure file-backed credentials
10
+ and run `codex login`:
11
+
12
+ ```toml
13
+ cli_auth_credentials_store = "file"
14
+ ```
15
+
16
+ Treat `auth.json` as a password. This provider calls the Codex-specific backend,
17
+ not the public OpenAI Platform API. It is experimental and intended for a
18
+ trusted, single-user service. Do not expose it to the public internet or an
19
+ untrusted multi-user environment.
20
+
21
+ ## Start the Example
22
+
23
+ From the Laiya repository root:
24
+
25
+ ```sh
26
+ export LAIYA_API_KEY="$(openssl rand -hex 32)"
27
+ bundle exec ruby examples/chatgpt/service.rb
28
+ ```
29
+
30
+ The example binds to `127.0.0.1:9293`. It requires the client to send the
31
+ separate `LAIYA_API_KEY` as a Bearer token. For remote clients, use TLS and a
32
+ private access-controlled network. See the [example files](https://github.com/socketry/laiya/tree/main/examples/chatgpt).
33
+
34
+ ## Client-Owned Tool Execution
35
+
36
+ Use `POST /v1/responses` for tool calling. Laiya preserves the Codex Responses
37
+ stream and response items. The client executes a returned function call and
38
+ sends its `function_call_output` back in the next request. Since Codex is used
39
+ statelessly, send the full input history, including the previous response's
40
+ reasoning and function-call items; `previous_response_id` is not supported.
41
+
42
+ The text-only `POST /v1/chat/completions` adapter rejects tool-enabled requests
43
+ so it cannot silently lose Codex reasoning state.
@@ -0,0 +1,49 @@
1
+ # Getting Started
2
+
3
+ This guide explains how to install Laiya, configure a provider, and make an OpenAI-compatible request.
4
+
5
+ ## Installation
6
+
7
+ Add Laiya to your application:
8
+
9
+ ```sh
10
+ bundle add laiya
11
+ ```
12
+
13
+ ## Configure a Provider
14
+
15
+ Laiya's Ruby API uses a `Laiya::Configuration` to connect model IDs to
16
+ providers. For an OpenAI Platform API key:
17
+
18
+ ```ruby
19
+ require "laiya"
20
+
21
+ configuration = Laiya::Configuration.build do |builder|
22
+ builder.provider :openai, Laiya::Provider::OpenAI.new(
23
+ api_key: ENV.fetch("OPENAI_API_KEY")
24
+ )
25
+ builder.model "gpt-4.1-mini", provider: :openai
26
+ builder.default_provider :openai
27
+ end
28
+
29
+ application = Laiya::Web::Application.new(configuration: configuration)
30
+ ```
31
+
32
+ The provider forwards `Protocol::HTTP::Request` and `Protocol::HTTP::Response`
33
+ objects using `Async::HTTP`. It does not parse OpenAI request or response bodies
34
+ in the direct proxy path.
35
+
36
+ For Ollama and multi-provider setups, see [Providers and Models](../providers-and-models/).
37
+
38
+ ## Run the API Service
39
+
40
+ For a local OpenAI API proxy, set the API key and start Laiya:
41
+
42
+ ```sh
43
+ export OPENAI_API_KEY="..."
44
+ bundle exec bin/laiya
45
+ ```
46
+
47
+ The service listens on `http://localhost:9292` by default. Configure `LAIYA_URL`
48
+ to change the endpoint. See [HTTP API](../http-api/) for supported routes and
49
+ streaming behavior.
@@ -0,0 +1,49 @@
1
+ # HTTP API
2
+
3
+ This guide explains how Laiya's OpenAI-compatible HTTP API handles routing, proxying, and streaming.
4
+
5
+ ## Run the Async Service
6
+
7
+ Laiya runs directly on `Async::HTTP::Server` under `Async::Service`; it does not
8
+ use Rack:
9
+
10
+ ```sh
11
+ bundle exec bin/laiya
12
+ ```
13
+
14
+ The default service listens on `http://localhost:9292`. `LAIYA_URL` configures
15
+ the endpoint. `bin/laiya` loads optional Ruby configuration paths supplied on
16
+ the command line before starting the service.
17
+
18
+ ## Endpoints
19
+
20
+ The API accepts OpenAI-compatible `Protocol::HTTP::Request` objects and returns
21
+ `Protocol::HTTP::Response` objects. Supported common routes include:
22
+
23
+ - `GET /v1/models`
24
+ - `POST /v1/chat/completions`
25
+ - `POST /v1/responses` when the configured provider supports it
26
+ - Other OpenAI-compatible paths are forwarded to the selected provider.
27
+
28
+ Configured model routes produce a Laiya model catalog. When a provider is the
29
+ default and no model catalog is configured, `GET /v1/models` is forwarded
30
+ upstream. See [Providers and Models](../providers-and-models/) for discovery.
31
+
32
+ ## Proxy and Model Routing
33
+
34
+ The built-in OpenAI provider is a direct HTTP proxy. It forwards the method,
35
+ path, headers, and request body, replacing authorization with its configured
36
+ upstream key and filtering hop-by-hop headers. It returns the upstream response
37
+ without parsing its body.
38
+
39
+ When a model route or discovery source is configured, Laiya buffers and rewinds
40
+ request bodies for known JSON completion endpoints to read the `model` field.
41
+ It forwards the original body unchanged. Streaming response bodies are not
42
+ buffered by the router.
43
+
44
+ ## Streaming
45
+
46
+ OpenAI-compatible upstream SSE responses pass through unchanged. Translating
47
+ providers may adapt streaming formats. The Codex Responses endpoint passes
48
+ native Responses SSE through, including tool-call events; Laiya does not execute
49
+ client tools.
data/guides/index.md ADDED
@@ -0,0 +1,14 @@
1
+ # Guides
2
+
3
+ Use these guides to set up Laiya, route OpenAI-compatible requests to providers,
4
+ and run the HTTP API.
5
+
6
+ ## Getting Started
7
+
8
+ - [Getting Started](getting-started/) — Install Laiya and make your first request.
9
+
10
+ ## Configuration
11
+
12
+ - [Providers and Models](providers-and-models/) — Configure provider routes and discover upstream models.
13
+ - [HTTP API](http-api/) — Run the async service and understand proxy and streaming behavior.
14
+ - [ChatGPT Codex](chatgpt-codex/) — Use the experimental Codex provider with ChatGPT login credentials.
data/guides/links.yaml ADDED
@@ -0,0 +1,8 @@
1
+ getting-started:
2
+ order: 1
3
+ providers-and-models:
4
+ order: 2
5
+ http-api:
6
+ order: 3
7
+ chatgpt-codex:
8
+ order: 4