Browse documentation

prelude.http

ランタイム組み込みの HTTP クライアント (fetch / post_json) — header と body の private-capable な提出面。

ランタイム組み込みの HTTP クライアント。呼び出しは runtime の http reactor に route する (FFI sidecar なし — mcp / webhook と同じ「runtime 内蔵の外部呼び出し」)。default import 経由で http. qualified に呼ぶ。external 呼び出しなので io を行い、呼び出し側の effect 行に io が 加わる。

private の情報流: 宛先サーバーへの提出面だけが通す

private な値がランタイムを出てよいのは、リクエストの宛先サーバーへ向かうときだけ。その意図的な 提出面 — header の値 body — は string of private で、secret (認証トークン、フォーム エンコードされた refresh_token など) をそのまま渡せる。両方とも 1 箇所の transport 境界で reveal され、そこでリクエストはプログラムが名指した唯一のサーバーへ発つ。

urlmethod は public な string のまま — private を渡すと型エラーになる。URL はログ・ キャッシュ・プロキシ・Referer ヘッダに漏れる、つまり宛先サーバー以外の場所にも流れるからだ。 レスポンスは public (declassify 済み) — サーバーの応答であって secret の関数ではないので、 private な body を持つリクエストの応答も汚染されない。

http.fetch_error

data fetch_error(message: string)

リクエストが完結しなかった: DNS 失敗、接続拒否、タイムアウト、途中でのランタイム再起動。fetch が 投げる。応答が届いた場合は (どんな status でも) エラーではない — status で分岐する。

http.status_error

data status_error(status: integer, body: string)

JSON API が非 2xx を返した。post_json が投げる (生の fetch は届いた応答をエラー扱いしない — この wrapper だけが JSON API の作法を前提にする)。

agent

http.fetch

external agent fetch(
  url: string,
  method: string,
  headers: record[string of private],
  body: string of private,
) -> { status: integer, headers: record[string], body: string } with prelude.throw[fetch_error] from "http"

urlmethod ("GET" / "POST" など) でリクエストする。各 header 値と body は secret でよい (両方とも url のサーバーにのみ提出され、urlmethod は public のまま)。応答の statusheaders (名前は小文字化、重複ヘッダは ", " で join)・body テキストを返す。リクエストが完結 しなければ fetch_error を投げる。

agent get_item(token: string) -> string with io | prelude.throw[http.fetch_error] {
  let response = http.fetch(
    url = "https://api.example.com/items",
    method = "GET",
    headers = record.set(target = record.empty(), key = "Authorization", value = "Bearer " ++ token),
    body = "",
  )
  match (response.status) {
    case 200 -> response.body
    case _ -> "request failed"
  }
}
  • Throws fetch_error (リクエストが完結しなかった)。

http.post_json

agent post_json(
  url: string,
  body: string of private,
  headers: record[string of private],
) -> string with io | prelude.throw[status_error | fetch_error] {
  let effective_headers = record.merge(
    left = record.set(target = record.empty(), key = "Content-Type", value = "application/json"),
    right = headers,
  )
  let response = fetch(url = url, method = "POST", headers = effective_headers, body = body)
  if (response.status >= 200 && response.status < 300) {
    response.body
  } else {
    prelude.throw(error = status_error(status = response.status, body = response.body))
  }
}

bodyapplication/json として url に POST する。body と各 header 値は secret でよい (両方 url のサーバーにのみ提出される)。Content-Type: application/json を既定で加えるが、呼び出し側が 同じキーを持てばそちらが勝つ。応答の body テキストを返す。JSON API 統合の一撃呼び出し: body を Katari で json として組み、POST し、応答を Katari で json として読む。

agent respond(prompt: string, api_key: string of private) -> string with io | prelude.throw[http.status_error | http.fetch_error] {
  let body = json.to_text(value = { model = "gpt-5", input = prompt })
  let headers = record.set(target = record.empty(), key = "Authorization", value = "Bearer " ++ api_key)
  http.post_json(url = "https://api.example.com/responses", body = body, headers = headers)
}
  • Throws status_error (非 2xx 応答)、fetch_error (リクエストが完結しなかった)。

関連