Skip to content

Azure Functions

The function is a thin HTTPS façade: it receives a request from Power Apps, Logic Apps, a Teams bot or a Service Bus queue, posts the canonical body to a private tachyone-serve, and returns the decision. Tachyone never has to be reachable from outside your network.

  • Transport: POST /v1/systemone from the function to Tachyone
  • Status: payload verified — the body and response below were executed against a local tachyone-serve; the Azure Functions host was not run in CI.

1. Where Tachyone runs

Option Latency profile When
Container Apps / AKS next to the function (VNet) GPU: single-digit ms p50 You need the fast path
Same VM, CPU encoder tens of ms Moderate volume, simplest ops
CPU + onnx extra portable, no torch Constrained runtimes

Azure Functions itself has no GPU, so the encoder runs in a neighbour service, not inside the function. Cold-start the model, not the function: preload the checkpoint and keep the container warm (TACHYONE_PRELOAD).

2. The function (Python v2 model)

import json
import os
import urllib.request

import azure.functions as func

app = func.FunctionApp()


@app.route(route="decide", auth_level=func.AuthLevel.FUNCTION)
def decide(req: func.HttpRequest) -> func.HttpResponse:
    try:
        payload = req.get_json()
    except ValueError:
        return func.HttpResponse(
            json.dumps({"error": {"code": "invalid_json", "message": "body is not JSON"}}),
            status_code=400,
            mimetype="application/json",
        )

    upstream = urllib.request.Request(
        f"{os.environ['TACHYONE_URL'].rstrip('/')}/v1/systemone",
        data=json.dumps(payload).encode(),
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    if key := os.environ.get("TACHYONE_API_KEY"):
        upstream.add_header("Authorization", f"Bearer {key}")

    try:
        with urllib.request.urlopen(upstream, timeout=15) as response:
            body = json.load(response)
    except urllib.error.HTTPError as exc:  # pass the contract's error through
        return func.HttpResponse(exc.read(), status_code=exc.code, mimetype="application/json")
    except (urllib.error.URLError, TimeoutError) as exc:
        return func.HttpResponse(
            json.dumps({"error": {"code": "unreachable", "message": str(exc)}}),
            status_code=502,
            mimetype="application/json",
        )

    return func.HttpResponse(json.dumps(body), status_code=200, mimetype="application/json")

Only stdlib is used, so the function needs no extra packages beyond azure-functions. It is a pass-through: the response body is the wire response, byte for byte.

local.settings.json:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "python",
    "TACHYONE_URL": "http://tachyone.internal:8000",
    "TACHYONE_API_KEY": "change-me"
  }
}

3. Call it with the canonical body

{
  "state": "Hi, we were charged twice for the March invoice and finance needs the duplicate reversed before the end of the quarter. Invoice number INV-2291.",
  "model": "tachyone-latest",
  "questions": {
    "queue": {
      "type": "choice",
      "instructions": "Which queue should receive this ticket first?",
      "criteria": {
        "billing-queue": "invoices, payments, refunds",
        "technical-queue": "bugs, outages, system errors",
        "sales-queue": "pricing, new contracts",
        "general-queue": "everything else"
      }
    }
  }
}

What the function returns (captured from a real encoder run against the same body):

{
  "model": "tachyone-latest",
  "answers": {
    "queue": {
      "type": "choice",
      "choice": "billing-queue",
      "probabilities": {
        "billing-queue": 1.0,
        "technical-queue": 0.0,
        "sales-queue": 0.0,
        "general-queue": 0.0
      },
      "confidence": 1.0
    }
  },
  "usage": {
    "input_tokens": 36,
    "output_tokens": 1
  }
}

Read it in a downstream Logic Apps expression: body('Decide')['answers']['queue']['choice'], and branch when body('Decide')['answers']['queue']['confidence'] is less than 0.6 — the System-2 handoff from the cookbook.

4. Trigger shapes that fit decisions

  • HTTP — synchronous classification inside an app or a Power App.
  • Service Bus / Queue — drain a ticket queue: one function invocation per message, or POST /predict/batch for a chunk of messages in one call.
  • Timer — periodic triage of accumulated items.

Failure handling

The function forwards Tachyone's contract errors untouched: 401 (bad key), 422 (body failed validation — check error.body.details), 429/529 (retry with exponential backoff + jitter). A 502 from the function means Tachyone itself was unreachable — alert on that separately from the contract statuses.