Python SDK
Installation
Section titled “Installation”uv add flow-likepip install flow-likeOptional extras
Section titled “Optional extras”| Extra | For | Install |
|-------|-----|---------|
| lance | create_lance_connection() | uv add flow-like[lance] |
| langchain | LangChain wrappers | uv add flow-like[langchain] |
Creating a Client
Section titled “Creating a Client”from flow_like import FlowLikeClient
# From environment variables (FLOW_LIKE_BASE_URL + FLOW_LIKE_PAT or FLOW_LIKE_API_KEY)client = FlowLikeClient()
# Explicit PATclient = FlowLikeClient( base_url="https://api.flow-like.com", pat="pat_myid.mysecret",)
# Explicit API keyclient = FlowLikeClient( base_url="https://api.flow-like.com", api_key="flk_appid.keyid.secret",)
# Auto-detect token typeclient = FlowLikeClient( base_url="https://api.flow-like.com", token="pat_myid.mysecret", # or "flk_...")Sync vs Async
Section titled “Sync vs Async”Every method has both a synchronous and an asynchronous variant. Async methods are prefixed with a:
# Syncllms = client.list_llms()
# Asyncllms = await client.alist_llms()Workflows
Section titled “Workflows”Trigger a workflow (streaming)
Section titled “Trigger a workflow (streaming)”Returns a Server-Sent Events stream:
for event in client.trigger_workflow("app-id", "board-id", "start-node-id", {"key": "value"}): print(event.data)Trigger a workflow (async)
Section titled “Trigger a workflow (async)”Returns immediately with a run ID for polling:
result = client.trigger_workflow_async("app-id", "board-id", "start-node-id", {"key": "value"})print(result.run_id, result.poll_token)Trigger an event
Section titled “Trigger an event”# Streamingfor event in client.trigger_event("app-id", "event-id", {"key": "value"}): print(event.data)
# Async invocationresult = client.trigger_event_async("app-id", "event-id")Execution Monitoring
Section titled “Execution Monitoring”# Check run statusstatus = client.get_run_status("run-id")print(status.status) # "running" | "completed" | "failed"
# Poll for execution eventspoll = client.poll_execution("poll-token", after_sequence=0, timeout=30)for event in poll.events: print(event)File Management
Section titled “File Management”# List filesfiles = client.list_files("app-id")
# Uploadclient.upload_file("app-id", open("data.csv", "rb"))
# Downloadcontent = client.download_file("app-id", "path/to/file.csv")
# Deleteclient.delete_file("app-id", "path/to/file.csv")Database / LanceDB
Section titled “Database / LanceDB”Credentials
Section titled “Credentials”# Get resolved credentials (uri + storage_options ready for LanceDB)info = client.get_db_credentials("app-id", access_mode="read")print(info.uri, info.storage_options)
# Get raw presign response (shared_credentials dict, db_path, etc.)raw = client.get_db_credentials_raw("app-id", table_name="_default", access_mode="write")Tables
Section titled “Tables”# List tablestables = client.list_tables("app-id")
# Query a tableresult = client.query_table("app-id", "my-table", {"filter": "col > 5", "limit": 10})LanceDB Connection
Section titled “LanceDB Connection”# Returns a ready-to-use lancedb.DBConnectiondb = client.create_lance_connection("app-id", access_mode="write")
# Use with the LanceDB APItable = db.open_table("my-embeddings")results = table.search([0.1, 0.2, ...]).limit(10).to_pandas()Access modes:
"read"(default) — read-only credentials"write"— full read-write credentials
Chat Completions
Section titled “Chat Completions”Use list_llms() to discover available model bit_id values.
# Non-streamingresult = client.chat_completions( messages=[{"role": "user", "content": "Explain quantum computing in one sentence."}], bit_id="bit-id-for-gpt4", temperature=0.7, max_tokens=200,)print(result.choices[0].message.content)Streaming
Section titled “Streaming”for event in client.chat_completions( messages=[{"role": "user", "content": "Hello!"}], bit_id="bit-id-for-gpt4", stream=True,): print(event.data, end="")Usage tracking
Section titled “Usage tracking”usage = client.get_usage()print(usage) # {"llm_price": ..., "embedding_price": ...}Embeddings
Section titled “Embeddings”Use list_embedding_models() to discover available embedding model bit_id values.
result = client.embed(bit_id="bit-id-for-embedding", input="Hello world")print(result.embeddings) # list[list[float]]
# Multiple inputsresult = client.embed(bit_id="bit-id-for-embedding", input=["Hello", "World"])Model Discovery
Section titled “Model Discovery”# List all remotely-available LLMsllms = client.list_llms()for m in llms: print(m.bit_id, m.name, m.provider_name, m.context_length)
# List all remotely-available embedding modelsembeddings = client.list_embedding_models()for m in embeddings: print(m.bit_id, m.name, m.vector_length)
# Search all bits by keywordbits = client.search_bits(search="llama", bit_types=["Llm"])
# Get a specific bit by IDbit = client.get_bit("some-bit-id")Each model returns a ModelInfo dataclass:
@dataclassclass ModelInfo: bit_id: str name: str description: str provider_name: str | None model_id: str | None context_length: int | None # LLMs only vector_length: int | None # Embeddings only languages: list[str] tags: list[str]Board Management
Section titled “Board Management”# List all boards in an appboards = client.list_boards("app-id")
# Get a specific boardboard = client.get_board("app-id", "board-id")
# Create or update a boardresult = client.upsert_board("app-id", "board-id", name="My Board", description="Does things")print(result.id)
# Delete a boardclient.delete_board("app-id", "board-id")
# Pre-run analysis (discover runtime variables)prerun = client.prerun_board("app-id", "board-id")print(prerun.runtime_variables)App Management
Section titled “App Management”apps = client.list_apps()app = client.get_app("app-id")new_app = client.create_app("My App", "Description")HTTP Sinks
Section titled “HTTP Sinks”response = client.trigger_http_sink( "app-id", "my/webhook/path", method="POST", body={"event": "user.created", "data": {"userId": "123"}},)Health Check
Section titled “Health Check”health = client.health()print(health.healthy) # TrueLangChain Integration
Section titled “LangChain Integration”Factory methods (recommended)
Section titled “Factory methods (recommended)”Create LangChain-compatible models from your existing client:
# Chat modelchat_model = client.as_langchain_chat( bit_id="your-model-bit-id", temperature=0.7, max_tokens=1024,)
# Embeddingsembeddings = client.as_langchain_embeddings(bit_id="your-embedding-bit-id")Standalone usage
Section titled “Standalone usage”from flow_like.langchain import FlowLikeChatModel, FlowLikeEmbeddings
chat_model = FlowLikeChatModel( base_url="https://api.flow-like.com", token="pat_myid.mysecret", bit_id="your-model-bit-id", temperature=0.7, max_tokens=1024,)
embeddings = FlowLikeEmbeddings( base_url="https://api.flow-like.com", token="pat_myid.mysecret", bit_id="your-embedding-bit-id",)With LangChain chains
Section titled “With LangChain chains”from langchain_core.prompts import ChatPromptTemplatefrom langchain_core.output_parsers import StrOutputParser
chat_model = client.as_langchain_chat("your-model-bit-id")
chain = ( ChatPromptTemplate.from_messages([ ("system", "You are a helpful assistant."), ("human", "{input}"), ]) | chat_model | StrOutputParser())
response = chain.invoke({"input": "What is Flow-Like?"})RAG with LangChain
Section titled “RAG with LangChain”embeddings = client.as_langchain_embeddings("your-embedding-bit-id")
# Use with any LangChain vector storevectors = embeddings.embed_documents([ "Flow-Like is a visual workflow engine.", "It runs on your device.",])query_vector = embeddings.embed_query("What runs locally?")Async support
Section titled “Async support”LangChain async methods work out of the box:
response = await chat_model.ainvoke("Hello!")vectors = await embeddings.aembed_documents(["text1", "text2"])Requirements
Section titled “Requirements”- Python >= 3.10
- httpx >= 0.27.0 (installed automatically)
Error Handling
Section titled “Error Handling”The SDK raises httpx.HTTPStatusError for non-2xx responses:
import httpx
try: result = client.get_app("nonexistent-id")except httpx.HTTPStatusError as e: print(f"HTTP {e.response.status_code}: {e.response.text}")