Hikigai

AppSDK

Application SDK

Lightweight SDK for invoking healthcare AI agents in your medical applications and EHR systems.HIPAA-ready, fast, and production-ready with streaming responses for clinical workflows and session support for patient consultations.

v0.1.4Python 3.10+StreamingSessionsWebSocket

Installation

Using pip

pip install hikigai-appsdk

Quick Start

from hikigai.appsdk import AppClient

# Initialize client (or set HIKIGAI_API_KEY / HIKIGAI_PROJECT_ID)
client = AppClient(
    api_key="your-api-key",
    project_id="your-project-id"
)

# Get an agent
agent = client.agent("medical-coder")

# Invoke the agent
response = agent.invoke("Patient presents with fever and cough...")
print(response.content)

# Stream responses
for chunk in agent.stream("Tell me about diabetes"):
    print(chunk, end="", flush=True)

# Session-based conversations
session_agent = agent.with_session("user-123")
session_agent.invoke("What is hypertension?")
session_agent.invoke("How is it treated?")  # Remembers context

# Live WebSocket session
import asyncio
async def live():
    async with agent.live_session() as session:
        await session.connect()
        await session.send_text("Hello")
        async for event in session:
            print(event)
asyncio.run(live())

Client Reference

class AppClient

AppClientCore

Main client for invoking AI agents. Manages authentication, HTTP connections, and provides access to deployed agents.

Constructor

client = AppClient(
    api_key: Optional[str] = None,        # API key (or HIKIGAI_API_KEY env var)
    project_id: Optional[str] = None,     # Project ID (or HIKIGAI_PROJECT_ID env var)
    base_url: Optional[str] = None,       # API endpoint (defaults to production)
    timeout: float = 30.0,                # Request timeout in seconds
    sona_url: Optional[str] = None,       # Optional SONA service URL
    sona_api_key: Optional[str] = None,   # Optional SONA API key
)

Methods

MethodReturnsDescription
agent(agent_id: str)RuntimeAgentGet agent by ID or slug for invocation
list_agents(category=None, tags=None, search=None)List[RuntimeAgent]List available agents with optional filters
invoke(agent_id, input, session_id=None, provider=None, model=None, connectors=None, plugin_context=None)InvokeResponseInvoke an agent without fetching RuntimeAgent first
live(agent_id, session_id=None, user_id='sdk-user', context=None)StreamSessionOpen a live WebSocket session for an agent
create_session(external_user_ref=None, ttl_seconds=None, app_id='_', metadata=None)SessionContextCreate a server-side session context store
session(session_id, app_id='_')SessionContextAttach to an existing session context
list_sessions(user_ref=None, limit=50, cursor=None)DictList session contexts
get_auth_token()Dict[str, Any]Exchange API key for a short-lived access token

Properties

identity()IdentityClientEnd-user auth (Cognito, MFA, QR, SSO)
sona()SONAClientSONA personalization (edits, preferences, suggestions)
storage()StorageClientApp object storage

Context Manager

with AppClient() as client:
    agent = client.agent("my-agent")
    response = agent.invoke("Hello")
# Resources automatically released

Agent Reference

class RuntimeAgent

RuntimeAgentModel

Represents a deployed agent ready for invocation. Provides methods for synchronous invocation, streaming, and session management.

Properties

PropertyTypeDescription
idstrUnique agent ID
namestrAgent name
slugstrURL-friendly slug
versionstrAgent version
descriptionstr | NoneAgent description
statusstrDeployment status ('active', 'pending', etc.)
endpointstr | NoneInvocation endpoint URL

Methods

MethodReturnsDescription
invoke(input, session_id=None, provider=None, model=None, timeout=None, connectors=None, plugin_context=None)InvokeResponseInvoke agent synchronously with optional session, MCP connectors, and plugins
stream(input, session_id=None, provider=None, model=None, connectors=None)Iterator[str]Stream agent response chunks in real-time (SSE)
with_session(session_id: str)RuntimeAgentCreate agent instance with persistent session
live_session(session_id=None)StreamSessionOpen a bidirectional WebSocket live session

Basic Usage

agent = client.agent("medical-coder")

# Synchronous invocation
response = agent.invoke("Patient has diabetes...")
print(response.content)

# With MCP connectors + SONA plugin context
response = agent.invoke(
    "Prepare patient chart",
    connectors={"epic-ehr": {"headers": {"Authorization": "Bearer ..."}}},
    plugin_context={"sona": {"user_id": "dr-smith-uuid"}},
)

# Streaming
for chunk in agent.stream("Explain hypertension"):
    print(chunk, end="")

# With session
response = agent.invoke("My name is Alice", session_id="user-123")
response = agent.invoke("What's my name?", session_id="user-123")  # Remembers "Alice"

Response Types

class InvokeResponse

InvokeResponsePydantic Model

Response from a synchronous agent invocation containing output content and metadata.

PropertyTypeDescription
content*strAgent output content
agent_idstrID of the agent that responded
agent_versionstr | NoneVersion of the agent
session_idstr | NoneSession ID if used
statusstrStatus (success, requires_human_review, etc.)
outputDict | NoneStructured agent output when available
confidenceClinicalConfidence | NoneClinical confidence score (0–1)
safety_flagsList[SafetyFlag]Safety flags (severity: low|medium|high|critical)
citationsList[ClinicalCitation]Evidence-based clinical citations
metadataInvocationMetadataInvocation metadata (latency, tokens, etc.)
messagestr | NoneOptional warning or info message
pluginsDict | NonePlugin metadata (e.g. plugins['sona'])

Example

response = agent.invoke("Hello")

print(response.content)                    # "Hi! How can I help you?"
print(response.status)                     # "success"
print(response.metadata.latency_ms)        # 234
print(response.metadata.tokens_used)       # 156
print(response.metadata.tools_called)      # ["web_search"]
print(response.plugins)                    # {"sona": {"output_id": "..."}}

class InvocationMetadata

InvocationMetadataPydantic Model

Metadata about an agent invocation including performance metrics and execution details.

invocation_idstrUnique invocation ID
latency_msint | NoneResponse latency in milliseconds
timestampdatetimeInvocation timestamp
statusstrStatus ('success', 'error', etc.)
tokens_usedint | NoneTotal tokens consumed
tools_calledList[str]List of tools used by agent
phi_redactedboolWhether PHI was redacted
trace_idstr | NoneTrace ID for troubleshooting

Live WebSocket Sessions

Bidirectional realtime sessions via StreamSession. Requires the optional websockets dependency.

import asyncio
from hikigai.appsdk import AppClient

async def main():
    client = AppClient(api_key="...", project_id="...")
    agent = client.agent("live-scribe")

    async with agent.live_session() as session:
        await session.connect()
        await session.start(modality="text")  # or "audio"
        await session.send_text("Patient presents with fever")
        # await session.send_audio(pcm_bytes, mime_type="audio/pcm;rate=16000")

        async for event in session:
            print(event)
            if event.get("type") == "turn_complete":
                break

        await session.end()
        await session.close()

asyncio.run(main())

Session Context Store

Server-side key/value context for apps (SessionContext), separate from conversation session_id memory on invoke.

# Create a context store
ctx = client.create_session(external_user_ref="dr-smith", ttl_seconds=3600)
ctx.set("patient_id", "MRN-12345")
ctx.set("visit_id", "V-99", if_match=None)

print(ctx.get("patient_id"))
print(ctx.get_all())
print(ctx.metadata())

# Attach later
ctx = client.session(ctx.session_id)
ctx.update({"chief_complaint": "chest pain"})
ctx.touch(ttl_seconds=7200)
ctx.delete("visit_id")
ctx.clear()

Session Management

Maintain conversation context across multiple invocations using sessions. Sessions enable multi-turn conversations where the agent remembers previous interactions.

Per-Invocation Sessions

Pass a session_id to each invocation to maintain context.

agent = client.agent("chatbot")

# First message
response1 = agent.invoke(
    "My name is Alice",
    session_id="user-123"
)

# Follow-up - agent remembers the name
response2 = agent.invoke(
    "What's my name?",
    session_id="user-123"
)
print(response2.content)  # "Your name is Alice"

Persistent Session Agent

Create a session-bound agent using with_session(). All invocations automatically use the same session.

agent = client.agent("assistant")

# Create session-bound agent
session_agent = agent.with_session("user-456")

# All invocations share the same session
session_agent.invoke("I live in New York")
session_agent.invoke("What's the weather like here?")  # Knows "here" = NYC

# Streaming works too
for chunk in session_agent.stream("Tell me more about my city"):
    print(chunk, end="")

Multiple Users

Maintain separate contexts for different users with different session IDs.

agent = client.agent("support-bot")

# User 1's conversation
user1 = agent.with_session("user-001")
user1.invoke("I'm having trouble logging in")

# User 2's isolated conversation
user2 = agent.with_session("user-002")
user2.invoke("How do I reset my password?")

# Contexts are completely separate
# User 1's issues don't affect User 2's conversation

Rooms

Multi-party realtime rooms via RoomSession(presence, publish, sequence tracking).

import asyncio
from hikigai.appsdk import RoomSession

async def main():
    room = RoomSession(
        base_url="https://api.hikigai.com",
        api_key="your-api-key",
        project_id="your-project-id",
    )
    async with room:
        await room.join("care-team-7")
        await room.publish("care-team-7", "note", {"text": "Labs ready"})
        await room.who("care-team-7")
        async for frame in room:
            print(frame)

asyncio.run(main())

Multi-Cloud Deployment

client.cloud exposes the deployment catalog and BYOC credentials. Apps, agents, and MCP servers deploy to GCP or AWS targets from the same catalog.

from hikigai.appsdk import AppClient

client = AppClient(api_key=..., project_id=...)

catalog = client.cloud.catalog(workload="app")
for service in catalog.services:
    print(service.id, service.display_name, service.regions[0].id)

result = client.cloud.validate_target(
    workload="app",
    service_id="gcp-cloud-run",
    region="europe-west1",
    config={"cpu": "2", "memory": "1Gi"},
)

Events & Webhooks

client.events is the SDK surface for the platform event bus — HMAC-signed webhooks (at-least-once) andevents.stream() (at-most-once).

sub = client.events.create_webhook(
    url="https://ci.example.com/hooks/hikigai",
    event_types=["agent.deployed", "agent.deleted"],
    description="deploy notifications",
)
print(sub.secret)  # shown once — store it

# Live stream
async with client.events.stream(patterns=["job.*", "agent.deployed"]) as stream:
    async for event in stream:
        print(event.type, event.data)

Storage

uploaded = client.storage.upload(
    app_id="app_123",
    file=open("audio.wav", "rb"),
    filename="audio.wav",
    content_type="audio/wav",
)
url = client.storage.signed_url("app_123", uploaded.object_id, ttl=3600)
client.storage.list("app_123")
client.storage.delete("app_123", uploaded.object_id)

SONA Personalization

Access via client.sona. Invoke with plugin_context={"sona": {"user_id": "..."}}to receive SONA metadata on response.plugins.

response = agent.invoke(
    "Draft discharge summary",
    plugin_context={"sona": {"user_id": "dr-smith-uuid"}},
)
output_id = response.plugins["sona"]["output_id"]

client.sona.submit_edit(output_id, "User-edited note text...")
client.sona.approve_output(output_id, user_id="dr-smith-uuid")
prefs = client.sona.get_preferences("dr-smith-uuid", agent_id=agent.id)
suggestions = client.sona.get_suggestions("dr-smith-uuid")

End-User Identity

Add signup, login, MFA, QR badge login, and SSO for your app's end users — clinicians, patients, staff. Each app is backed by a dedicated AWS Cognito user pool the platform provisions for you; login returns standard OIDC tokens. Everything is reached through client.identity.

class IdentityClient

client.identityCore

End-user authentication and account management for the app's Cognito pool, plus QR/PIN badge credentials. Access it as a property on AppClient.

Account & auth methods

MethodReturnsDescription
signup(app_id, email, password, first_name=None, last_name=None, role=None, attributes=None, metadata=None)dictRegister a new end user. attributes = Cognito fields (e.g. phone_number); metadata = opaque app JSON on EndUser.
confirm(app_id, email, code)dictConfirm a signup with the emailed code
login(app_id, email, password)dictAuthenticate — returns OIDC tokens or an MFA challenge
refresh(app_id, refresh_token)dictExchange a refresh token for fresh tokens
logout(app_id, email)dictGlobally sign the user out
forgot_password(app_id, email)dictRequest a password-reset code (emailed by Cognito)
reset_password(app_id, email, code, new_password)dictSet a new password with the emailed code
change_password(app_id, access_token, current_password, new_password)dictChange password for a logged-in user
update_user(user_id, **fields)dictUpdate end-user profile (role, name, metadata, is_active). Email cannot be changed.
list_app_users(app_id, limit=50, offset=0)list[dict]List the app's end users
delete_app_user(app_id, email)NoneDelete a user from the pool

MFA, QR & SSO

MethodReturnsDescription
mfa_associate(app_id, access_token)dictBegin TOTP enrollment — returns secret for a QR code
mfa_verify(app_id, access_token, code, device_name=None)dictVerify the first code to finish enrollment
mfa_set_preference(app_id, access_token, enabled=True)dictTurn TOTP MFA on or off
mfa_respond(app_id, email, session, code)dictComplete a login that returned an MFA challenge
issue_qr_login(app_id, email)dictIssue a QR badge credential for a user
qr_login(app_id, qr_payload)dictExchange a scanned QR payload for tokens
configure_sso(app_id, provider_name, provider_type, …)dictAdd a Google/Okta/SAML/OIDC provider
sso_authorize_url(app_id, redirect_uri, provider=None)dictBuild the hosted-UI authorize URL
sso_exchange(app_id, code, redirect_uri)dictExchange the callback code for tokens

Setup (one-time, per app)

enable_identity(app_id, mode='managed', enabled_methods=…)dictProvision the app's Cognito pool
get_identity_config(app_id)dictGet config + provisioning status
byo_setup()dictCloudFormation params for bring-your-own Cognito
byo_test(role_arn, region=None)dictVerify a cross-account role before enabling BYO

Sign up, confirm, log in

attributes are Cognito profile fields (string values — e.g. phone_number in E.164). metadata is opaque app JSON stored on the platform EndUser (and as custom:metadata when supported). They are not interchangeable.

from hikigai.appsdk import AppClient

client = AppClient(api_key="hikigai_...", project_id="proj_123")

# 1. Register the end user
client.identity.signup(
    app_id="app_123",
    email="clinician@hospital.com",
    password="Temp#Pass2026",
    first_name="Asha",
    last_name="Mehta",
    role="clinician",
    # attributes → Cognito fields (strings only; e.g. phone_number in E.164)
    attributes={"phone_number": "+14155552671"},
    # metadata → opaque app JSON on EndUser (and custom:metadata when supported)
    metadata={"doctorID": "6278383837"},
)

# 2. Confirm (skip if the pool auto-confirms)
client.identity.confirm("app_123", "clinician@hospital.com", code="123456")

# 3. Log in -> OIDC tokens
tokens = client.identity.login("app_123", "clinician@hospital.com", "Temp#Pass2026")
print(tokens["access_token"])

# 4. Refresh later
fresh = client.identity.refresh("app_123", tokens["refresh_token"])

Forgot password

# 1. Request reset code (Cognito emails it)
client.identity.forgot_password("app_123", "clinician@hospital.com")

# 2. User enters code + new password
client.identity.reset_password(
    "app_123", "clinician@hospital.com", code="123456", new_password="NewTemp#Pass2026"
)

Change password (logged in)

tokens = client.identity.login("app_123", "clinician@hospital.com", "Temp#Pass2026")
client.identity.change_password(
    "app_123",
    access_token=tokens["access_token"],
    current_password="Temp#Pass2026",
    new_password="NewTemp#Pass2026",
)

Update role / profile

# user_id from signup response or list_app_users()
client.identity.update_user("eu_abc123", role="doctor", first_name="Asha", last_name="Mehta")

Multi-factor login

# Login may return an MFA challenge instead of tokens
result = client.identity.login("app_123", "clinician@hospital.com", "Temp#Pass2026")

if result.get("status") == "challenge":
    code = input("Enter your authenticator code: ")
    tokens = client.identity.mfa_respond(
        app_id="app_123",
        email="clinician@hospital.com",
        session=result["session"],
        code=code,
    )
    print(tokens["access_token"])

QR badge login

# Admin: issue a badge for the user (payload shown once)
badge = client.identity.issue_qr_login("app_123", "clinician@hospital.com")
print(badge["qr_payload"])   # encode into the printed badge

# Kiosk: scan the badge, exchange it for tokens
tokens = client.identity.qr_login("app_123", scanned_payload)
signup vs create_user

Use identity.signup() for real Cognito accounts (password, MFA, SSO). Use identity.create_user() only for the lightweight QR/PIN badge flow (no password), paired with verify_qr() / verify_pin().

Complete Examples

Basic Agent Invocation

from hikigai.appsdk import AppClient

client = AppClient()

# Get agent
agent = client.agent("customer-support")

# Invoke
response = agent.invoke("How do I reset my password?")
print(response.content)
print(f"Latency: {response.metadata.latency_ms}ms")

Streaming Responses

agent = client.agent("content-writer")

# Stream in real-time
print("Agent response: ", end="")
for chunk in agent.stream("Write a paragraph about AI"):
    print(chunk, end="", flush=True)
print()  # New line

Session-Based Chat Application

import uuid
from hikigai.appsdk import AppClient

def chat_session(user_id: str):
    """Start a chat session for a user."""
    client = AppClient()
    agent = client.agent("chatbot")
    
    # Create session-bound agent
    session = agent.with_session(user_id)
    
    print("Chat started. Type 'exit' to quit.")
    
    while True:
        user_input = input("You: ")
        if user_input.lower() == "exit":
            break
        
        # Invoke with session context
        response = session.invoke(user_input)
        print(f"Bot: {response.content}")

# Start chat for a user
chat_session(f"user-{uuid.uuid4()}")

Listing and Filtering Agents

client = AppClient()

# List all agents
agents = client.list_agents()
for agent in agents:
    print(f"{agent.name} (v{agent.version}): {agent.status}")

# Filter by category
medical_agents = client.list_agents(category="Medical Coding")

# Search
search_results = client.list_agents(search="diagnosis")

Error Handling

from hikigai.appsdk import (
    AppClient,
    AgentNotFoundError,
    InvocationError,
    RateLimitError
)

client = AppClient()

try:
    agent = client.agent("my-agent")
    response = agent.invoke("Hello")
    print(response.content)
    
except AgentNotFoundError:
    print("Agent not found. Check the agent ID.")
    
except InvocationError as e:
    print(f"Invocation failed: {e}")
    
except RateLimitError:
    print("Rate limit exceeded. Please wait.")
    
except Exception as e:
    print(f"Unexpected error: {e}")

Error Handling

AgentNotFoundError

Raised when the requested agent doesn't exist

from hikigai.appsdk import AgentNotFoundError

try:
    agent = client.agent("nonexistent-agent")
except AgentNotFoundError as e:
    print(f"Agent not found: {e}")

InvocationError

Raised when agent invocation fails

from hikigai.appsdk import InvocationError

try:
    response = agent.invoke("Complex query...")
except InvocationError as e:
    print(f"Invocation failed: {e}")

AuthenticationError

Raised when the API key is invalid or missing

from hikigai.appsdk import AuthenticationError, ConfigurationError

try:
    client = AppClient(api_key="invalid-key", project_id="proj_123")
except (AuthenticationError, ConfigurationError) as e:
    print(f"Auth/config failed: {e}")

RateLimitError

Raised when API rate limit is exceeded

from hikigai.appsdk import RateLimitError
import time

try:
    response = agent.invoke("Query")
except RateLimitError as e:
    print(f"Rate limit hit. Retry after: {e.retry_after}s")
    time.sleep(e.retry_after)
    response = agent.invoke("Query")  # Retry