swift-apple-intelligence-grpc/docs/grpc-client-guide.md
Mathias Beaulieu-Duncan b8854e4e12 Add gRPC client guide and update runner docs for local network
- Add comprehensive gRPC client guide with examples for grpcurl, Python,
  Node.js, Go, and Swift clients including streaming and authentication
- Update macOS runner setup with instructions for connecting to local
  Gitea instance running in Docker on Linux (network config, firewall,
  DNS setup)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 04:40:23 -05:00

8.6 KiB

gRPC Client Guide

This guide explains how to connect to and interact with the Apple Intelligence gRPC server from various programming languages.

Server Information

  • Default Host: 0.0.0.0 (all interfaces)
  • Default Port: 50051
  • Protocol: gRPC over HTTP/2 (plaintext)
  • Service: appleintelligence.AppleIntelligence

Available Methods

Method Type Description
Health Unary Check server and model status
Complete Unary Generate a complete response
StreamComplete Server Streaming Generate response with streaming tokens

Proto Definition

syntax = "proto3";
package appleintelligence;

service AppleIntelligence {
  rpc Health(HealthRequest) returns (HealthResponse);
  rpc Complete(CompletionRequest) returns (CompletionResponse);
  rpc StreamComplete(CompletionRequest) returns (stream CompletionChunk);
}

message HealthRequest {}

message HealthResponse {
  bool healthy = 1;
  string model_status = 2;
}

message CompletionRequest {
  string prompt = 1;
  optional float temperature = 2;
  optional int32 max_tokens = 3;
}

message CompletionResponse {
  string id = 1;
  string text = 2;
  string finish_reason = 3;
}

message CompletionChunk {
  string id = 1;
  string delta = 2;
  bool is_final = 3;
  optional string finish_reason = 4;
}

Testing with grpcurl

grpcurl is a command-line tool for interacting with gRPC servers.

Installation

# macOS
brew install grpcurl

# Linux
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest

Health Check

grpcurl -plaintext \
  -proto Sources/Protos/apple_intelligence.proto \
  localhost:50051 \
  appleintelligence.AppleIntelligence/Health

Response:

{
  "healthy": true,
  "modelStatus": "available"
}

Complete (Non-Streaming)

grpcurl -plaintext \
  -proto Sources/Protos/apple_intelligence.proto \
  -d '{"prompt": "What is 2 + 2?"}' \
  localhost:50051 \
  appleintelligence.AppleIntelligence/Complete

Response:

{
  "id": "abc123",
  "text": "2 + 2 equals 4.",
  "finishReason": "stop"
}

StreamComplete (Streaming)

grpcurl -plaintext \
  -proto Sources/Protos/apple_intelligence.proto \
  -d '{"prompt": "Tell me a short story"}' \
  localhost:50051 \
  appleintelligence.AppleIntelligence/StreamComplete

Response (multiple chunks):

{"id": "xyz789", "delta": "Once", "isFinal": false}
{"id": "xyz789", "delta": " upon", "isFinal": false}
{"id": "xyz789", "delta": " a", "isFinal": false}
{"id": "xyz789", "delta": " time", "isFinal": false}
...
{"id": "xyz789", "delta": "", "isFinal": true, "finishReason": "stop"}

Python Client

Installation

pip install grpcio grpcio-tools

Generate Python Code from Proto

python -m grpc_tools.protoc \
  -I. \
  --python_out=. \
  --grpc_python_out=. \
  Sources/Protos/apple_intelligence.proto

Basic Client

import grpc
import apple_intelligence_pb2 as pb
import apple_intelligence_pb2_grpc as rpc

# Connect to server
channel = grpc.insecure_channel('localhost:50051')
stub = rpc.AppleIntelligenceStub(channel)

# Health check
health = stub.Health(pb.HealthRequest())
print(f"Healthy: {health.healthy}")
print(f"Model Status: {health.model_status}")

# Non-streaming completion
response = stub.Complete(pb.CompletionRequest(
    prompt="What is the capital of France?"
))
print(f"Response: {response.text}")

# Streaming completion
for chunk in stub.StreamComplete(pb.CompletionRequest(
    prompt="Write a haiku about coding"
)):
    if chunk.delta:
        print(chunk.delta, end='', flush=True)
print()  # Newline at end

Async Client

import asyncio
import grpc.aio
import apple_intelligence_pb2 as pb
import apple_intelligence_pb2_grpc as rpc

async def main():
    async with grpc.aio.insecure_channel('localhost:50051') as channel:
        stub = rpc.AppleIntelligenceStub(channel)

        # Streaming with async
        async for chunk in stub.StreamComplete(pb.CompletionRequest(
            prompt="Explain quantum computing"
        )):
            if chunk.delta:
                print(chunk.delta, end='', flush=True)
        print()

asyncio.run(main())

Node.js / TypeScript Client

Installation

npm install @grpc/grpc-js @grpc/proto-loader

Client

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');

// Load proto
const packageDef = protoLoader.loadSync('Sources/Protos/apple_intelligence.proto');
const proto = grpc.loadPackageDefinition(packageDef).appleintelligence;

// Create client
const client = new proto.AppleIntelligence(
  'localhost:50051',
  grpc.credentials.createInsecure()
);

// Health check
client.Health({}, (err, response) => {
  console.log('Healthy:', response.healthy);
  console.log('Model Status:', response.modelStatus);
});

// Non-streaming completion
client.Complete({ prompt: 'Hello, how are you?' }, (err, response) => {
  console.log('Response:', response.text);
});

// Streaming completion
const stream = client.StreamComplete({ prompt: 'Tell me a joke' });
stream.on('data', (chunk) => {
  process.stdout.write(chunk.delta);
});
stream.on('end', () => {
  console.log('\nStream ended');
});

Go Client

Installation

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

Generate Go Code

protoc --go_out=. --go-grpc_out=. Sources/Protos/apple_intelligence.proto

Client

package main

import (
    "context"
    "fmt"
    "io"
    "log"

    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"

    pb "your-module/appleintelligence"
)

func main() {
    // Connect
    conn, err := grpc.Dial("localhost:50051",
        grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    client := pb.NewAppleIntelligenceClient(conn)
    ctx := context.Background()

    // Health check
    health, _ := client.Health(ctx, &pb.HealthRequest{})
    fmt.Printf("Healthy: %v\n", health.Healthy)

    // Streaming
    stream, _ := client.StreamComplete(ctx, &pb.CompletionRequest{
        Prompt: "What is AI?",
    })

    for {
        chunk, err := stream.Recv()
        if err == io.EOF {
            break
        }
        fmt.Print(chunk.Delta)
    }
    fmt.Println()
}

Swift Client

Using grpc-swift

import GRPC
import NIO

let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
defer { try! group.syncShutdownGracefully() }

let channel = try GRPCChannelPool.with(
    target: .host("localhost", port: 50051),
    transportSecurity: .plaintext,
    eventLoopGroup: group
)

let client = Appleintelligence_AppleIntelligenceAsyncClient(channel: channel)

// Health check
let health = try await client.health(Appleintelligence_HealthRequest())
print("Healthy: \(health.healthy)")

// Streaming
for try await chunk in client.streamComplete(Appleintelligence_CompletionRequest.with {
    $0.prompt = "Hello!"
}) {
    print(chunk.delta, terminator: "")
}

Authentication

If the server is configured with an API key, include it in the metadata:

grpcurl

grpcurl -plaintext \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{"prompt": "Hello"}' \
  localhost:50051 \
  appleintelligence.AppleIntelligence/Complete

Python

metadata = [('authorization', 'Bearer YOUR_API_KEY')]
response = stub.Complete(request, metadata=metadata)

Node.js

const metadata = new grpc.Metadata();
metadata.add('authorization', 'Bearer YOUR_API_KEY');
client.Complete({ prompt: 'Hello' }, metadata, callback);

Network Access

Local Network

To connect from other devices on your network:

  1. Find the server Mac's IP address:

    ipconfig getifaddr en0
    
  2. Connect using the IP:

    grpcurl -plaintext 192.168.1.100:50051 ...
    

Firewall

If connections fail, check macOS firewall:

  • System Settings → Network → Firewall
  • Allow incoming connections for the app

Troubleshooting

Connection Refused

  • Verify server is running
  • Check host and port
  • Ensure firewall allows connections

Deadline Exceeded

  • Server may be overloaded
  • Increase timeout in client
  • Check network latency

Unauthenticated

  • API key is required but not provided
  • API key is incorrect
  • Check Authorization header format

Model Not Available

  • Apple Intelligence is not enabled on the server Mac
  • Check System Settings → Apple Intelligence & Siri
  • Ensure macOS 26+ and Apple Silicon