Skip to content

Commit 301180f

Browse files
committed
refactor: split request option section builders
1 parent f62d169 commit 301180f

11 files changed

Lines changed: 648 additions & 558 deletions

File tree

lib/codex_pooler/gateway/payloads/request_options.ex

Lines changed: 27 additions & 388 deletions
Large diffs are not rendered by default.
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
defmodule CodexPooler.Gateway.Payloads.RequestOptions.Continuity do
2+
@moduledoc false
3+
4+
alias CodexPooler.Gateway.Payloads.RequestOptions.Normalization
5+
6+
@session_header_sources [
7+
"x-codex-window-id",
8+
"x-codex-session-id",
9+
"session-id",
10+
"x-session-id",
11+
"x-session-affinity",
12+
"session_id",
13+
"x-codex-conversation-id"
14+
]
15+
16+
defstruct [
17+
:accepted_turn_state,
18+
:previous_response_id,
19+
:response_id,
20+
:session_header,
21+
:session_header_source,
22+
:session_key,
23+
:conversation_key,
24+
:owner_instance_id,
25+
:bridge_owner_lease_ttl_seconds,
26+
:reconnect_window_seconds,
27+
:codex_session,
28+
:codex_turn_id,
29+
:authenticated_owner_attach
30+
]
31+
32+
@type t :: %__MODULE__{
33+
accepted_turn_state: String.t() | nil,
34+
previous_response_id: String.t() | nil,
35+
response_id: String.t() | nil,
36+
session_header: String.t() | nil,
37+
session_header_source: String.t() | nil,
38+
session_key: String.t() | nil,
39+
conversation_key: String.t() | nil,
40+
owner_instance_id: String.t() | nil,
41+
bridge_owner_lease_ttl_seconds: pos_integer() | nil,
42+
reconnect_window_seconds: non_neg_integer() | nil,
43+
codex_session: term(),
44+
codex_turn_id: Ecto.UUID.t() | nil,
45+
authenticated_owner_attach: boolean()
46+
}
47+
48+
@spec build(map() | keyword()) :: t()
49+
def build(opts) do
50+
opts = Map.new(opts)
51+
52+
%__MODULE__{
53+
accepted_turn_state: Map.get(opts, :accepted_turn_state),
54+
previous_response_id: Map.get(opts, :previous_response_id),
55+
response_id: Map.get(opts, :response_id),
56+
session_header: Map.get(opts, :session_header),
57+
session_header_source: session_header_source(Map.get(opts, :session_header_source)),
58+
session_key: Map.get(opts, :session_key),
59+
conversation_key: Map.get(opts, :conversation_key),
60+
owner_instance_id: Map.get(opts, :owner_instance_id),
61+
bridge_owner_lease_ttl_seconds:
62+
Normalization.optional_positive_integer(Map.get(opts, :bridge_owner_lease_ttl_seconds)),
63+
reconnect_window_seconds:
64+
Normalization.optional_non_negative_integer(Map.get(opts, :reconnect_window_seconds)),
65+
codex_session: Map.get(opts, :codex_session),
66+
codex_turn_id: Map.get(opts, :codex_turn_id),
67+
authenticated_owner_attach: Map.get(opts, :authenticated_owner_attach, false) == true
68+
}
69+
end
70+
71+
@spec update(t(), map() | keyword()) :: t()
72+
def update(%__MODULE__{} = continuity, updates) do
73+
updates
74+
|> Map.new()
75+
|> Normalization.normalize_optional_update(
76+
:bridge_owner_lease_ttl_seconds,
77+
&Normalization.optional_positive_integer/1
78+
)
79+
|> Normalization.normalize_optional_update(
80+
:reconnect_window_seconds,
81+
&Normalization.optional_non_negative_integer/1
82+
)
83+
|> Normalization.normalize_optional_update(:session_header_source, &session_header_source/1)
84+
|> then(&struct!(continuity, &1))
85+
end
86+
87+
@spec session_header_source(term()) :: String.t() | nil
88+
def session_header_source(value) when is_atom(value) do
89+
value
90+
|> Atom.to_string()
91+
|> session_header_source()
92+
end
93+
94+
def session_header_source(value) when is_binary(value) do
95+
value = value |> String.trim() |> String.downcase()
96+
97+
if value in @session_header_sources do
98+
value
99+
end
100+
end
101+
102+
def session_header_source(_value), do: nil
103+
end
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
defmodule CodexPooler.Gateway.Payloads.RequestOptions.FileBridgeContext do
2+
@moduledoc false
3+
4+
alias CodexPooler.Gateway.Payloads.RequestOptions.Normalization
5+
6+
defstruct [
7+
:operation,
8+
:endpoint,
9+
:route_metadata,
10+
:pool_upstream_assignment_id,
11+
:upstream_identity_id,
12+
:defer_create_request,
13+
:finalize_retry_timeout_ms,
14+
:finalize_retry_interval_ms,
15+
forwarded_headers: []
16+
]
17+
18+
@type t :: %__MODULE__{
19+
operation: atom() | String.t() | nil,
20+
endpoint: String.t() | nil,
21+
route_metadata: map() | nil,
22+
pool_upstream_assignment_id: Ecto.UUID.t() | nil,
23+
upstream_identity_id: Ecto.UUID.t() | nil,
24+
defer_create_request: boolean() | nil,
25+
finalize_retry_timeout_ms: non_neg_integer() | nil,
26+
finalize_retry_interval_ms: non_neg_integer() | nil,
27+
forwarded_headers: [{String.t(), String.t()}]
28+
}
29+
30+
@spec build(map() | keyword()) :: t()
31+
def build(opts) do
32+
opts = Map.new(opts)
33+
34+
%__MODULE__{
35+
operation: Map.get(opts, :file_bridge_operation),
36+
endpoint: Map.get(opts, :file_bridge_endpoint),
37+
route_metadata: Map.get(opts, :file_bridge_route_metadata),
38+
forwarded_headers: Normalization.forwarded_headers(Map.get(opts, :forwarded_headers, [])),
39+
pool_upstream_assignment_id: Map.get(opts, :pool_upstream_assignment_id),
40+
upstream_identity_id: Map.get(opts, :upstream_identity_id),
41+
defer_create_request: Map.get(opts, :defer_file_create_request),
42+
finalize_retry_timeout_ms:
43+
Normalization.optional_non_negative_integer(Map.get(opts, :finalize_retry_timeout_ms)),
44+
finalize_retry_interval_ms:
45+
Normalization.optional_non_negative_integer(Map.get(opts, :finalize_retry_interval_ms))
46+
}
47+
end
48+
49+
@spec update(t(), map() | keyword()) :: t()
50+
def update(%__MODULE__{} = file_bridge, updates) do
51+
updates
52+
|> Map.new()
53+
|> Normalization.normalize_optional_update(
54+
:forwarded_headers,
55+
&Normalization.forwarded_headers_update/1
56+
)
57+
|> Normalization.normalize_optional_update(
58+
:finalize_retry_timeout_ms,
59+
&Normalization.optional_non_negative_integer/1
60+
)
61+
|> Normalization.normalize_optional_update(
62+
:finalize_retry_interval_ms,
63+
&Normalization.optional_non_negative_integer/1
64+
)
65+
|> then(&struct!(file_bridge, &1))
66+
end
67+
end
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
defmodule CodexPooler.Gateway.Payloads.RequestOptions.Normalization do
2+
@moduledoc false
3+
4+
@spec optional_positive_integer(term()) :: pos_integer() | nil
5+
def optional_positive_integer(value) when is_integer(value) and value > 0, do: value
6+
def optional_positive_integer(_value), do: nil
7+
8+
@spec optional_non_negative_integer(term()) :: non_neg_integer() | nil
9+
def optional_non_negative_integer(value) when is_integer(value) and value >= 0, do: value
10+
def optional_non_negative_integer(_value), do: nil
11+
12+
@spec forwarded_headers(term()) :: [{String.t(), String.t()}]
13+
def forwarded_headers(headers) when is_list(headers) do
14+
Enum.filter(headers, fn
15+
{name, value} -> is_binary(name) and is_binary(value)
16+
_other -> false
17+
end)
18+
end
19+
20+
def forwarded_headers(_headers), do: []
21+
22+
@spec forwarded_headers_update(term()) :: [{String.t(), String.t()}] | nil
23+
def forwarded_headers_update(headers) do
24+
case forwarded_headers(headers) do
25+
[] -> nil
26+
headers -> headers
27+
end
28+
end
29+
30+
@spec safe_endpoint(term()) :: String.t() | nil
31+
def safe_endpoint(value) when is_binary(value) do
32+
value = value |> String.trim() |> String.slice(0, 160)
33+
34+
if String.starts_with?(value, "/") and value != "/" do
35+
value
36+
else
37+
nil
38+
end
39+
end
40+
41+
def safe_endpoint(_value), do: nil
42+
43+
@spec normalize_optional_update(map(), atom(), (term() -> term())) :: map()
44+
def normalize_optional_update(updates, key, normalizer) when is_map(updates) and is_atom(key) do
45+
if Map.has_key?(updates, key) do
46+
case normalizer.(Map.fetch!(updates, key)) do
47+
nil -> Map.delete(updates, key)
48+
value -> Map.put(updates, key, value)
49+
end
50+
else
51+
updates
52+
end
53+
end
54+
end
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
defmodule CodexPooler.Gateway.Payloads.RequestOptions.OpenAICompatibility do
2+
@moduledoc false
3+
4+
alias CodexPooler.Gateway.Payloads.RequestOptions.Normalization
5+
6+
defstruct public_openai_responses_stream: false,
7+
public_openai_chat_stream: false,
8+
collect_openai_response_stream: false,
9+
collect_openai_image_stream: false,
10+
openai_chat_payload: nil,
11+
source_endpoint: nil,
12+
translated_endpoint: nil
13+
14+
@type t :: %__MODULE__{
15+
public_openai_responses_stream: boolean(),
16+
public_openai_chat_stream: boolean(),
17+
collect_openai_response_stream: boolean(),
18+
collect_openai_image_stream: boolean(),
19+
openai_chat_payload: map() | nil,
20+
source_endpoint: String.t() | nil,
21+
translated_endpoint: String.t() | nil
22+
}
23+
24+
@spec build(map() | keyword()) :: t()
25+
def build(opts) do
26+
opts = Map.new(opts)
27+
28+
%__MODULE__{
29+
public_openai_responses_stream: Map.get(opts, :public_openai_responses_stream, false),
30+
public_openai_chat_stream: Map.get(opts, :public_openai_chat_stream, false),
31+
collect_openai_response_stream: Map.get(opts, :collect_openai_response_stream, false),
32+
collect_openai_image_stream: Map.get(opts, :collect_openai_image_stream, false),
33+
openai_chat_payload: Map.get(opts, :openai_chat_payload),
34+
source_endpoint: Normalization.safe_endpoint(Map.get(opts, :openai_source_endpoint)),
35+
translated_endpoint: Normalization.safe_endpoint(Map.get(opts, :openai_translated_endpoint))
36+
}
37+
end
38+
39+
@spec mark_origin(t(), String.t(), String.t()) :: t()
40+
def mark_origin(%__MODULE__{} = compatibility, source_endpoint, translated_endpoint)
41+
when is_binary(source_endpoint) and is_binary(translated_endpoint) do
42+
%__MODULE__{
43+
compatibility
44+
| source_endpoint: Normalization.safe_endpoint(source_endpoint),
45+
translated_endpoint: Normalization.safe_endpoint(translated_endpoint)
46+
}
47+
end
48+
49+
@spec metadata(t()) :: map()
50+
def metadata(%__MODULE__{} = compatibility) do
51+
metadata =
52+
%{
53+
"surface" => surface(compatibility),
54+
"source_endpoint" => compatibility.source_endpoint,
55+
"translated_endpoint" => compatibility.translated_endpoint
56+
}
57+
|> Enum.reject(fn {_key, value} -> is_nil(value) end)
58+
|> Map.new()
59+
60+
if map_size(metadata) == 0 do
61+
%{}
62+
else
63+
%{"openai_compatibility" => metadata}
64+
end
65+
end
66+
67+
defp surface(%__MODULE__{source_endpoint: endpoint}) when is_binary(endpoint), do: "openai_v1"
68+
defp surface(_compatibility), do: nil
69+
end
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
defmodule CodexPooler.Gateway.Payloads.RequestOptions.PayloadContext do
2+
@moduledoc false
3+
4+
defstruct [:media_upload, :forced_transcription_model]
5+
6+
@type t :: %__MODULE__{
7+
media_upload: map() | nil,
8+
forced_transcription_model: String.t() | nil
9+
}
10+
11+
@spec build(map() | keyword()) :: t()
12+
def build(opts) do
13+
opts = Map.new(opts)
14+
15+
%__MODULE__{
16+
media_upload: Map.get(opts, :media_upload),
17+
forced_transcription_model: Map.get(opts, :forced_transcription_model)
18+
}
19+
end
20+
end
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
defmodule CodexPooler.Gateway.Payloads.RequestOptions.RuntimeContext do
2+
@moduledoc false
3+
4+
alias CodexPooler.Gateway.Payloads.RequestOptions.Normalization
5+
alias CodexPooler.Gateway.RequestCompression.Metadata, as: RequestCompressionMetadata
6+
7+
defstruct [:now, :interrupt_reason, :gateway_debug_payload, :payload_compression]
8+
9+
@type t :: %__MODULE__{
10+
now: DateTime.t() | nil,
11+
interrupt_reason: String.t() | nil,
12+
gateway_debug_payload: map() | nil,
13+
payload_compression: map() | nil
14+
}
15+
16+
@spec build(map() | keyword()) :: t()
17+
def build(opts) do
18+
opts = Map.new(opts)
19+
20+
%__MODULE__{
21+
now: Map.get(opts, :now),
22+
interrupt_reason: Map.get(opts, :interrupt_reason) || Map.get(opts, :reason),
23+
gateway_debug_payload: Map.get(opts, :gateway_debug_payload),
24+
payload_compression:
25+
RequestCompressionMetadata.runtime_metadata(Map.get(opts, :payload_compression))
26+
}
27+
end
28+
29+
@spec update(t(), map() | keyword()) :: t()
30+
def update(%__MODULE__{} = runtime, updates) do
31+
updates
32+
|> Map.new()
33+
|> Normalization.normalize_optional_update(
34+
:payload_compression,
35+
&RequestCompressionMetadata.runtime_metadata/1
36+
)
37+
|> then(&struct!(runtime, &1))
38+
end
39+
end

0 commit comments

Comments
 (0)