Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/sdk-js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Import **`GladiaClient`** and create an instance.

Provide an API key with **`apiKey`** or the **`GLADIA_API_KEY`** environment variable. [Get your API key here](https://docs.gladia.io/chapters/introduction/getting-started) in under a minute.

You can also set **`GLADIA_API_URL`** and **`GLADIA_REGION`** (`eu-west` / `us-west`).
You can also set **`GLADIA_API_URL`**. For live sessions, **`GLADIA_REGION`** (`eu-west` / `us-west`) is passed only on session creation (`POST /v2/live`).

### Node.js / Browser (ESM)

Expand Down
6 changes: 3 additions & 3 deletions packages/sdk-js/src/network/httpClient.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { deepMergeObjects, sleep } from '../helpers.js'
import type { HttpRetryOptions } from '../types.js'
import type { HttpRetryOptions, QueryParams } from '../types.js'
import { initFetch } from './iso-fetch.js'
import type { Headers } from './types.js'

Expand Down Expand Up @@ -115,7 +115,7 @@ type RequestOptions = Omit<RequestInit, 'method' | 'headers'> & {
export type HttpClientOptions = {
baseUrl: string | URL
headers?: Headers
queryParams?: Record<string, string>
queryParams?: QueryParams
retry: Required<HttpRetryOptions>
timeout: number
}
Expand Down Expand Up @@ -153,7 +153,7 @@ function isAbortError(error: unknown): boolean {
export class HttpClient {
private baseUrl: string | URL
private defaultHeaders?: Headers
private defaultQueryParams?: Record<string, string>
private defaultQueryParams?: QueryParams

private retry: Required<HttpRetryOptions>
private timeout: number
Expand Down
17 changes: 14 additions & 3 deletions packages/sdk-js/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,17 @@ export type LiveV2Timeouts = {
getFile?: number
}

/**
* Region for live session creation (POST /v2/live).
*/
export type Region = 'eu-west' | 'us-west'
Comment thread
egenthon-cmd marked this conversation as resolved.

/**
* Default HTTP query parameters attached to every request from an HTTP client.
* Do not put `region` here — it is only supported on live session creation (POST /v2/live).
*/
export type QueryParams = Record<string, string>

/**
* Options for the Gladia Client.
*/
Expand All @@ -115,11 +126,11 @@ export type GladiaClientOptions = {
apiUrl?: string

/**
* Region to use.
* Region for live session creation (POST /v2/live). Other routes do not support this param.
*
* If not provided, the client will take the environment variable GLADIA_REGION and, if not provided either, it will default to 'eu-west'.
* If not provided, the client will take the environment variable GLADIA_REGION.
*/
region?: 'eu-west' | 'us-west'
region?: Region

/**
* Custom headers to add to the HTTP requests.
Expand Down
5 changes: 5 additions & 0 deletions packages/sdk-js/src/v2/live/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { InternalGladiaClientOptions } from '../../internal_types.js'
import { HttpClient } from '../../network/httpClient.js'
import { WebSocketClient } from '../../network/wsClient.js'
import type { Region } from '../../types.js'
import type { LiveV2InitRequest, LiveV2InitResponse, LiveV2Response } from './generated-types.js'
import { LiveV2Session } from './session.js'
import type { LiveV2ConnectSessionOptions } from './types.js'
Expand All @@ -12,15 +13,18 @@ export class LiveV2Client {
private httpClient: HttpClient
private webSocketClient: WebSocketClient
private readonly liveTimeouts: InternalGladiaClientOptions['liveTimeouts']
private readonly region?: Region

constructor(options: InternalGladiaClientOptions) {
const httpBaseUrl = new URL(options.apiUrl)
httpBaseUrl.protocol = httpBaseUrl.protocol.replace(/^ws/, 'http')
this.liveTimeouts = options.liveTimeouts
this.region = options.region
this.httpClient = new HttpClient({
baseUrl: httpBaseUrl,
headers: options.httpHeaders,
...(options.region ? { queryParams: { region: options.region } } : {}),

retry: options.httpRetry,
timeout: options.httpTimeout,
})
Expand All @@ -37,6 +41,7 @@ export class LiveV2Client {
startSession(options: LiveV2InitRequest): LiveV2Session {
return new LiveV2Session({
options,
region: this.region,
httpClient: this.httpClient,
webSocketClient: this.webSocketClient,
})
Expand Down
19 changes: 19 additions & 0 deletions packages/sdk-js/src/v2/live/session.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,23 @@ describe('LiveV2Session connectSession', () => {
})
expect(session.sessionId).toBe('created-session-id')
})

it('passes region only on POST /v2/live', async () => {
const session = new LiveV2Session({
options: { sample_rate: 16000 },
region: 'us-west',
httpClient,
webSocketClient,
})

await tick()

expect(mockHttpPost).toHaveBeenCalledWith(
'/v2/live?region=us-west',
expect.objectContaining({
body: expect.stringContaining('"sample_rate":16000'),
})
)
expect(session.sessionId).toBe('created-session-id')
})
})
13 changes: 12 additions & 1 deletion packages/sdk-js/src/v2/live/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { EventEmitter } from 'eventemitter3'
import { concatArrayBuffer, toUint8Array } from '../../helpers.js'
import { HttpClient } from '../../network/httpClient.js'
import { WebSocketClient, WebSocketSession, WS_STATES } from '../../network/wsClient.js'
import type { Region } from '../../types.js'
import type {
LiveV2InitRequest,
LiveV2InitResponse,
Expand Down Expand Up @@ -36,18 +37,24 @@ export class LiveV2Session {

private _status: LiveV2SessionStatus = 'starting'

private readonly region?: Region

constructor({
options,
region,
existingSession,
httpClient,
webSocketClient,
}: {
options: LiveV2InitRequest
/** Region query param for POST /v2/live only. Ignored when attaching to an existing session. */
region?: Region
existingSession?: LiveV2InitResponse
httpClient: HttpClient
webSocketClient: WebSocketClient
}) {
this.sessionOptions = options
this.region = region
this.httpClient = httpClient
this.webSocketClient = webSocketClient
this.abortController = new AbortController()
Expand Down Expand Up @@ -132,7 +139,11 @@ export class LiveV2Session {

private async initSession(): Promise<LiveV2InitResponse> {
try {
return await this.httpClient.post<LiveV2InitResponse>(`/v2/live`, {
// region is only supported on session creation (POST /v2/live)
const initUrl = this.region
? `/v2/live?region=${encodeURIComponent(this.region)}`
: '/v2/live'
return await this.httpClient.post<LiveV2InitResponse>(initUrl, {
signal: this.abortController.signal,
headers: {
'Content-Type': 'application/json',
Expand Down
4 changes: 3 additions & 1 deletion packages/sdk-js/src/v2/prerecorded/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { sleep } from '../../helpers.js'
import type { InternalGladiaClientOptions } from '../../internal_types.js'
import { HttpClient } from '../../network/httpClient.js'
import type { QueryParams } from '../../types.js'
import type {
PreRecordedV2AudioUploadResponse,
PreRecordedV2InitTranscriptionRequest,
Expand Down Expand Up @@ -37,10 +38,11 @@ export class PreRecordedV2Client {
const httpBaseUrl = new URL(options.apiUrl)
httpBaseUrl.protocol = httpBaseUrl.protocol.replace(/^ws/, 'http')
this.prerecordedTimeouts = options.prerecordedTimeouts
const queryParams: QueryParams = {}
this.httpClient = new HttpClient({
baseUrl: httpBaseUrl,
headers: options.httpHeaders,
...(options.region ? { queryParams: { region: options.region } } : {}),
queryParams,
retry: options.httpRetry,
timeout: options.httpTimeout,
})
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk-python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Import **`GladiaClient`** and create an instance.

Provide an API key with **`api_key`** or the **`GLADIA_API_KEY`** environment variable. [Get your API key](https://docs.gladia.io/chapters/introduction/getting-started) in under a minute.

You can also set **`GLADIA_API_URL`** and **`GLADIA_REGION`** (`eu-west` / `us-west`).
You can also set **`GLADIA_API_URL`**. For live sessions, **`GLADIA_REGION`** (`eu-west` / `us-west`) is passed only on session creation (`POST /v2/live`).

### Sync client

Expand Down
4 changes: 4 additions & 0 deletions packages/sdk-python/src/gladiaio_sdk/client_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
# Region parameter
Region = Literal["eu-west", "us-west"]

# Default HTTP query parameters attached to every request from an HTTP client.
QueryParams = dict[str, str]
Comment thread
egenthon-cmd marked this conversation as resolved.

# Default timeouts (seconds) for general HTTP / WebSocket clients.
DEFAULT_HTTP_TIMEOUT: float = 10
DEFAULT_WS_TIMEOUT: float = 10
Expand Down Expand Up @@ -103,6 +106,7 @@ class GladiaClientOptions:

api_key: str | None = os.environ.get("GLADIA_API_KEY")
api_url: str = os.environ.get("GLADIA_API_URL", "https://api.gladia.io")
# Only applied to live session creation (POST /v2/live). Other routes ignore it.
region: Region | None = cast(Region | None, os.environ.get("GLADIA_REGION"))
http_headers: dict[str, str] = field(default_factory=dict)
http_retry: HttpRetryOptions = HttpRetryOptions()
Expand Down
36 changes: 29 additions & 7 deletions packages/sdk-python/src/gladiaio_sdk/network/helper.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit


def matches_status(status: int, rules: list[int | tuple[int, int]] | None) -> bool:
if not rules:
return False
Expand All @@ -13,12 +16,31 @@ def matches_status(status: int, rules: list[int | tuple[int, int]] | None) -> bo


def build_url(base_url: str, url: str) -> str:
# If already absolute, return as is
"""Join ``base_url`` and ``url``, keeping any query string at the end.

Absolute ``url`` values are returned unchanged. Path segments are concatenated
(same as before) so proxy path prefixes are preserved, but base and relative
query params are merged onto the final URL instead of being left mid-path.
"""
if url.startswith(("ws://", "wss://", "http://", "https://")):
return url
base = base_url
if base.endswith("/") and url.startswith("/"):
return base + url[1:]
if base.endswith("/") or url.startswith("/"):
return base + url
return f"{base}/{url}"

base = urlsplit(base_url)
rel = urlsplit(url)

base_path = base.path
rel_path = rel.path
if base_path.endswith("/") and rel_path.startswith("/"):
path = base_path + rel_path[1:]
elif base_path.endswith("/") or rel_path.startswith("/"):
path = base_path + rel_path
elif base_path:
path = f"{base_path}/{rel_path}"
else:
path = rel_path

params = parse_qsl(base.query, keep_blank_values=True)
params.extend(parse_qsl(rel.query, keep_blank_values=True))
query = urlencode(params)

return urlunsplit((base.scheme, base.netloc, path, query, rel.fragment or base.fragment))
6 changes: 3 additions & 3 deletions packages/sdk-python/src/gladiaio_sdk/network/http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import httpx

from gladiaio_sdk.client_options import HttpRetryOptions
from gladiaio_sdk.client_options import HttpRetryOptions, QueryParams
from gladiaio_sdk.network.helper import matches_status

_schema_field_names_cache: dict[str, frozenset[str]] = {}
Expand Down Expand Up @@ -313,7 +313,7 @@ def __init__(
self,
base_url: str,
headers: dict[str, str],
query_params: dict[str, str],
query_params: QueryParams,
retry: HttpRetryOptions,
timeout: float,
) -> None:
Expand Down Expand Up @@ -442,7 +442,7 @@ def __init__(
self,
base_url: str,
headers: dict[str, str],
query_params: dict[str, str],
query_params: QueryParams,
retry: HttpRetryOptions,
timeout: float,
) -> None:
Expand Down
9 changes: 9 additions & 0 deletions packages/sdk-python/src/gladiaio_sdk/v2/live/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import dataclasses
from collections.abc import Callable
from typing import Any, Literal, Protocol, TypeVar, overload
from urllib.parse import urlencode

from gladiaio_sdk.client_options import Region
from gladiaio_sdk.v2.live.types import (
LiveV2ConnectedMessage,
LiveV2ConnectingMessage,
Expand Down Expand Up @@ -62,6 +64,13 @@ def with_acknowledgments_enabled(options: LiveV2InitRequest) -> LiveV2InitReques
return dataclasses.replace(options, messages_config=msg_cfg)


def build_live_init_url(region: Region | None = None) -> str:
"""Build POST /v2/live URL. ``region`` is only supported on this route."""
if region:
return f"/v2/live?{urlencode({'region': region})}"
return "/v2/live"


def parse_ws_message(raw: Any) -> LiveV2WebSocketMessage:
text = raw.decode("utf-8") if isinstance(raw, (bytes, bytearray)) else str(raw)
return create_live_v2_web_socket_message_from_json(text)
Expand Down
11 changes: 6 additions & 5 deletions packages/sdk-python/src/gladiaio_sdk/v2/live/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from typing import TYPE_CHECKING, final
from urllib.parse import urlparse

from gladiaio_sdk.client_options import GladiaClientOptions
from gladiaio_sdk.client_options import GladiaClientOptions, QueryParams
from gladiaio_sdk.network import AsyncHttpClient, WebSocketClient
from gladiaio_sdk.v2.core import V2JobCore
from gladiaio_sdk.v2.live.async_session import LiveV2AsyncSession
Expand All @@ -20,9 +20,7 @@ def __init__(self, options: GladiaClientOptions) -> None:
base_http_url = urlparse(options.api_url)
base_http_url = base_http_url._replace(scheme=re.sub(r"^ws", "http", base_http_url.scheme))

query_params: dict[str, str] = {}
if options.region:
query_params["region"] = options.region
query_params: QueryParams = {}

self._http_client = AsyncHttpClient(
base_url=base_http_url.geturl(),
Expand All @@ -45,7 +43,10 @@ def __init__(self, options: GladiaClientOptions) -> None:

def start_session(self, options: LiveV2InitRequest) -> LiveV2AsyncSession:
return LiveV2AsyncSession(
options=options, http_client=self._http_client, ws_client=self._ws_client
options=options,
http_client=self._http_client,
ws_client=self._ws_client,
region=self._options.region,
)

def connect_session(self, options: LiveV2ConnectSessionOptions) -> LiveV2AsyncSession:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from pyee.asyncio import AsyncIOEventEmitter

from gladiaio_sdk.client_options import Region
from gladiaio_sdk.v2.live.types import (
LiveV2ConnectedMessage,
LiveV2ConnectingMessage,
Expand All @@ -24,6 +25,7 @@
)
from ._helpers import (
LiveV2SessionEventsMixin,
build_live_init_url,
emit_session_ending_events,
emit_started_if_needed,
maybe_emit_start_session_message,
Expand Down Expand Up @@ -60,10 +62,12 @@ def __init__(
http_client: AsyncHttpClient,
ws_client: WebSocketClient,
existing_session: LiveV2InitResponse | None = None,
region: Region | None = None,
) -> None:
self._options = options
self._http_client = http_client
self._ws_client = ws_client
self._region: Region | None = region

self._abort = asyncio.Event()
self._event_emitter = AsyncIOEventEmitter()
Expand Down Expand Up @@ -121,7 +125,7 @@ def end_session(self) -> None:
async def _init_session(self) -> LiveV2InitResponse:
try:
options = with_acknowledgments_enabled(self._options)
resp = await self._http_client.post("/v2/live", json=options.to_dict())
resp = await self._http_client.post(build_live_init_url(self._region), json=options.to_dict())
return LiveV2InitResponse.from_json(resp.content)
except Exception as err:
_ = self._event_emitter.emit("error", err)
Expand Down
Loading
Loading