Python client for the Honeyframe data platform — a dataikuapi-style SDK
Project description
honeyframeapi
A dataikuapi-style Python client for the Honeyframe data platform
(Honeyframe Platform / platform.hubstudio.id). Script the platform from a
notebook: read data into pandas, build dashboards and cards, browse datasets
and connectors.
If you've used dataikuapi, this will feel identical — construct one client,
then navigate to project / dataset / dashboard handles.
import dataikuapi
client = dataikuapi.DSSClient(host, api_key) # Dataiku
import honeyframeapi
client = honeyframeapi.HoneyframeClient(host, username=..., password=..., project_id=1) # Honeyframe
Install
pip install -e sdk/python # core (httpx only)
pip install -e "sdk/python[pandas]" # + pandas for get_dataframe()/sql()
Authenticate
Three ways, in precedence order:
import honeyframeapi
# 1. email / password (works against any deployment today; auto-refreshes the 8h JWT)
client = honeyframeapi.HoneyframeClient(
"https://platform.hubstudio.id",
username="you@hubstudio.id", password="…",
project_id=1,
)
# 2. a token you already hold — a JWT, or (preferred for automation) a
# long-lived Personal Access Token minted from the UI
# (avatar menu → "Personal access tokens") or via client.create_pat()
client = honeyframeapi.HoneyframeClient("https://platform.hubstudio.id", token="hf_…", project_id=1)
# 3. from environment variables
# HONEYFRAME_URL (or HUB_API_BASE), HONEYFRAME_TOKEN,
# HONEYFRAME_USERNAME/PASSWORD (or HUB_USERNAME/PASSWORD), HONEYFRAME_PROJECT_ID
client = honeyframeapi.HoneyframeClient.from_env()
Vibe-code the warehouse, end to end
One governed loop, and it adapts to the deployment — see
examples/vibe_code_end_to_end.py:
proj = client.get_project(1)
proj.engine() # 'dbt' or 'native' — does this install have dbt?
proj.upload_dataframe("raw", df, materialize=True) # push local data in
if proj.uses_dbt():
proj.dbt.create_model("stg_x", sql="SELECT … FROM {{ ref('raw') }}")
proj.dbt.run(select="stg_x") # author + build a dbt model
df = proj.sql("SELECT * FROM stg_x") # read results back
proj.get_dataset("raw").upstream() # trace lineage
# …then share it: dashboard → webapp → token-gated public link
dash = proj.create_dashboard("Demo")
dash.add_card("By region", "bar", "SELECT region, total FROM stg_x")
app = client.publish_webapp("demo", "Demo", config={"pages": [...]})
print(app.create_public_link(expires_days=7)["share_url"])
(On a native install the authoring lines change — see proj.engine() — but the
upload → read → dashboard → webapp → share legs are identical.)
Read data into pandas
# ad-hoc SQL — the analogue of dwh-adira's run_sql()
df = client.sql("""
SELECT booking_source, COUNT(*) AS n
FROM marts.fact_appointment
GROUP BY booking_source ORDER BY n DESC
""")
# or read a whole dataset
proj = client.get_project(1)
df = proj.get_dataset("fact_appointment").get_dataframe()
head = proj.get_dataset("fact_appointment").head(20)
stats = proj.get_dataset("fact_appointment").profile()
Build a dashboard from code
This replaces the hand-rolled requests script in dwh-adira
(scripts/upload_dashboards.py) — same card dicts, ergonomic API:
proj = client.get_project(1)
dash = proj.create_dashboard("Appointments Overview",
description="Booking channels & volume")
dash.add_card("Total Appointments", "kpi",
"SELECT COUNT(*) AS n FROM marts.fact_appointment",
config={"color_theme": "ocean"}, size_x=6, size_y=2)
dash.add_card("By Booking Source", "donut",
"SELECT booking_source AS name, COUNT(*) AS value "
"FROM marts.fact_appointment GROUP BY booking_source",
config={"nameField": "name", "valueField": "value"})
results = dash.execute_all()
print("Open:", dash.url)
Porting an existing upload_dashboards.py block? Pass the dicts straight through:
dash.add_cards(DASHBOARDS[0]["cards"]) # list of {"title","card_type","sql_query","card_config",…}
Publish a webapp + share it by link
The last mile: wrap dashboard cards in a webapp and hand a stakeholder a URL they can open without an account (token-gated, PII-masked).
app = client.publish_webapp(
"ops-overview", "Ops Overview",
config={"pages": [{"key": "home", "title": "Home", "blocks": [
{"kind": "card_ref", "dashboard_id": dash.dashboard_id,
"card_id": dash.cards[0].card_id},
]}]},
)
link = app.create_public_link(label="exec share", expires_days=30)
print(link["token"]) # shown ONCE — store it
print(app.public_url(link["token"], # anonymous URL on the SaaS host
host="https://hospital.hubstudio.id"))
# later: audit + revoke
for l in app.public_links():
print(l["label"], l["status"], l["view_count"])
app.revoke_public_link(link["id"])
publish_webapp is project-scoped — set project_id on the client first.
Build a multi-page app from code (no hand-rolled config)
The builders compose pages, blocks and filters for you — including the two
flexible block kinds: code_card (a governed read-only SELECT → chart, run
server-side with an 8s timeout + 5k-row cap) and html_block (freeform
HTML/CSS/JS; mode="sandboxed" runs arbitrary JS — even a canvas game — in an
opaque-origin iframe with a CSP that blocks outbound network, so it can't read
your JWT or phone home). Add data_sql= to a sandboxed block and the server
runs that governed SELECT and postMessages the rows into the fenced iframe —
the "data bridge", so your custom JS renders real DWH data without any API
access of its own.
from honeyframeapi import (
page, nav_item, code_card, html_block, card_ref,
filters, date_range_filter, branch_filter,
)
app = client.create_webapp(
"ops-overview", "Ops Overview",
pages=[page(
"home", "Home",
blocks=[
card_ref(dash.dashboard_id, dash.cards[0].card_id, w=12, h=4),
code_card("SELECT branch_name, visits FROM marts.v_branch_visits "
"WHERE :period_start <= visit_date AND visit_date <= :period_end",
"bar", x=12, w=12, h=4, title="Visits by branch"),
html_block("<canvas id=game></canvas><script>/* … */</script>",
mode="sandboxed", w=12, h=8, title="Mini-game"),
# DATA BRIDGE: a fenced sandboxed block that renders REAL data —
# the server runs data_sql and postMessages the rows into the iframe
# (the JS listens for e.data.source === 'honeyframe').
html_block("<div id=viz></div><script>addEventListener('message',e=>{"
"if(e.data?.source==='honeyframe')draw(e.data.rows)})</script>",
mode="sandboxed", w=12, h=8, title="Custom viz",
data_sql="SELECT branch_name, visits FROM marts.v_branch_visits"),
html_block("<i>internal note</i>", public_hidden=True, w=12, h=2),
],
# page-level filters bound into the SQL above by param name
filters=filters(
period=date_range_filter(), # :period_start/:period_end
branch=branch_filter("branch_id_codes", mode="multi",
options_sql="SELECT afya_code AS value, branch_name AS label "
"FROM marts.v_hospital_360 WHERE total_appointments > 0"),
),
)],
nav=[nav_item("home", "Home", icon="gauge")],
publish=True,
)
# incremental edits read-modify-write the stored config for you
app.add_page(page("detail", "Detail", [code_card("SELECT 1", "kpi")]),
nav=nav_item("detail", "Detail"), publish=True)
app.set_theme({"primaryColor": "#0E7C66"})
app.remove_page("detail")
Scenarios, jobs & recipes (dataikuapi parity)
Closes the gap behind dwh-adira's run_instinct_daily_scenario.py and
scaffold_* scripts:
proj = client.get_project(1)
# scenarios — trigger and poll like dataikuapi's scenario.run()
sc = proj.get_scenario("daily_pipeline")
final = sc.run_and_wait() # polls to terminal state
print(final["status"], final.get("steps_failed"))
# jobs / pipeline
runs = client.jobs.list_runs(status="failed")
run = client.jobs.trigger("dbt_run", select="int_appointments_unioned+")
print(client.jobs.logs("dbt_run", lines=100))
# recipes (dbt-engine build)
proj.get_recipe("dim_patient").build(downstream=True, wait=True)
# datasets — create like create_sql_table_dataset()
proj.create_dataset("ekko", connector_id=5,
connection_config={"schema_name": "RAW_RSL", "table_name": "EKKO"})
# datasets — surface an existing raw table in the picker (no data copied)
proj.register_source_table("staging_inbd", "stgops_master_dokter", connector_id=5)
proj.register_schema("staging_inbd", connector_id=5) # or the whole schema, idempotent
# connectors (org-level)
conn = client.create_connector("SAP Prod", "postgresql", config={"host": "…"})
print(conn.test())
# publish a data API + mint a key
asset = client.create_asset("data_api", "appointments", "Appointments API", config={...})
asset.publish()
print(asset.create_api_key(name="readonly").key) # dk_… shown once
# chat with an agent
print(client.get_agent(42).ask("Cancellations last month?")["answer"])
CLI
Installing the package also installs a honeyframe command. Flags go after
the subcommand (kubectl/gh style).
export HONEYFRAME_URL=https://platform.hubstudio.id
export HONEYFRAME_TOKEN=hf_… # a PAT (preferred) or JWT
honeyframe whoami
honeyframe projects -f csv
honeyframe datasets -p 1
honeyframe sql "SELECT COUNT(*) FROM marts.fact_appointment" -p 1 -f csv
honeyframe engine -p 1 # dbt or native install?
honeyframe upload sales sales.csv --materialize -p 1
honeyframe lineage fact_appointment --down -p 1
honeyframe export fact_appointment out.parquet --max-rows 100000 -p 1
honeyframe pat create --name laptop --expires-days 90
honeyframe scenario run daily_pipeline -p 1 --wait
Resource map
| Handle | Get it from | Key methods |
|---|---|---|
HoneyframeClient |
constructor | sql, list_projects, get_project, get_dashboard, jobs, list_connectors, create_connector, get_connector, list_my_credentials, list_knowledge_bases, create_knowledge_base, get_knowledge_base, kb_connector_options, llm_mesh_overview, get_llm_policy, set_llm_policy, llm_mesh_usage, list_card_templates, create_card_template, list_collections, create_collection, update_collection, delete_collection, list_assets, create_asset, get_asset, list_webapps, get_webapp, publish_webapp, list_agents, get_agent, chat, get_auth_info |
Project |
client.get_project(id_or_slug) |
list_datasets, get_dataset, create_dataset, import_dataset, register_source_table, register_schema, sql, create_dashboard, list_scenarios, get_scenario, create_scenario, get_recipe, run_dbt, change_recipe_engines, create_bundle, list_bundles, bundle, list_signoffs, list_bundle_activations, dbt → DbtIDE, get_preferences, set_preferences, list_connectors |
Dataset |
project.get_dataset(name) |
get_dataframe, head, profile, get_metadata, ask, materialize, versions, preview_version, rollback_version, update_version, delete |
Dashboard |
project.create_dashboard(...) / client.get_dashboard(id) |
add_card, add_cards, execute_all (filters=[...]), cards, update, move_to_collection, public_links, create_public_link, revoke_public_link, export_pdf, shares, add_share, update_share, remove_share, revisions, create_revision, get_revision, diff_revisions, restore_revision, undo, undo_card, url |
Card |
dashboard.cards / dashboard.add_card(...) |
execute, update, delete, unlink_template |
CardTemplate |
client.create_card_template(...) / client.list_card_templates() |
update, delete, insert_into, link_into, preview, duplicate, versions, get_version, restore_version |
Connector |
client.create_connector(...) / client.get_connector(id) |
test, update, delete, set_my_credentials, delete_my_credentials, rotate, secret_history, rollback_secret |
Scenario |
project.get_scenario(id_or_name) |
run, run_and_wait, get_last_runs, update, delete, list_triggers, add_trigger, update_trigger, delete_trigger, list_reporters, add_reporter, update_reporter, delete_reporter, test_reporter |
Recipe |
project.get_recipe(model_name) |
get_metadata, runs, build, engine_status, save_prepare_steps, save |
Bundle |
project.create_bundle(...) / project.bundle(version) |
download, activate, deploy, push, governance_status, request_signoff, submit_feedback, submit_for_approval, approve, reject, abandon |
KnowledgeBase |
client.create_knowledge_base(...) / client.get_knowledge_base(id) |
update, delete, docs, upload_doc, remove_doc, embed, sync, clear, search, check_vector_store, create_tool, link_agent, unlink_agent, usage |
JobsAPI |
client.jobs |
list_runs, get_run, wait, abort_run, trigger, logs |
PublishedAsset |
client.create_asset(...) / client.get_asset(id) |
publish, unpublish, update, create_api_key, list_api_keys, revoke_api_key, versions, rollback, diff_versions |
APIService |
client.create_api_service(...) / client.get_api_service(id) |
add_endpoint, set_endpoints, publish, create_key, call, test, versions, rollback, diff_versions |
Webapp |
client.publish_webapp(...) / client.get_webapp(key) |
config, public_links, create_public_link, revoke_public_link, versions, rollback, diff_versions, url, public_url |
DbtIDE |
project.dbt() |
write_file, read_file, create_model, list_models, compile, run, test, build, git_commit |
Agent |
client.get_agent(id) |
ask, ask_stream |
Global-filter builders (from honeyframeapi import ...): dashboard_filter, filter_between, filter_compare, filter_in — build the filters=[...] list Dashboard.execute_all applies to every card whose SQL references the filter column.
Errors
Every failure raises a subclass of honeyframeapi.HoneyframeError:
HoneyframeAuthError (401/403), HoneyframeNotFoundError (404),
HoneyframeValidationError (400/409/422), HoneyframeAPIError (other non-2xx),
each carrying .status_code and .detail.
Scope
Shipped: auth (login + token), projects, datasets→pandas + create/import/ materialize + snapshot versions & as-of time-travel reads, ad-hoc SQL, dashboards + cards, connectors (full CRUD + test), knowledge banks (RAG build loop) + LLM Mesh governance/spend, scenarios (run/wait/history + triggers + reporters), project bundles + Govern-style deployment sign-offs, jobs + pipeline triggers + logs, recipes (inspect + dbt build + author prepare steps + per-recipe engine selection), published assets + data-API-key minting, agent chat. This covers the dataikuapi surface used across the dwh-adira scripts.
Personal Access Tokens (hf_…) are first-class: mint from the settings UI or
client.create_pat(name, expires_days=…), then authenticate with
token="hf_…". They're independently revocable, scoped to the minting user,
and don't carry the 8h JWT expiry — the right credential for cron/automation.
Still planned: richer recipe authoring (SQL/Python recipe payloads beyond prepare steps).
client.sql()note: there's no public raw-SQL endpoint yet, sosql()runs through a hidden per-project scratch dashboard card (zero backend change). It honors the platform's per-card row cap — for full-table extracts useDataset.get_dataframe(). When aPOST /api/sqlendpoint lands, only the internals change;sql()'s signature is stable.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file honeyframeapi-0.1.7.tar.gz.
File metadata
- Download URL: honeyframeapi-0.1.7.tar.gz
- Upload date:
- Size: 126.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0717dac8359f8688b665e9d83aedee8c8adf6f95f3af2407269650b23504f69d
|
|
| MD5 |
93b19d856fffe4436130447d74fe6cf1
|
|
| BLAKE2b-256 |
4bfe78b83ada45bf9ca71eb645206c3fa0b0ecaf9b8f1641d971070a4f7f33f7
|
File details
Details for the file honeyframeapi-0.1.7-py3-none-any.whl.
File metadata
- Download URL: honeyframeapi-0.1.7-py3-none-any.whl
- Upload date:
- Size: 95.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2895f1cd00a8d3b96b855f98a6462dc59095fc8250e43ca00caea72583d5fcc8
|
|
| MD5 |
9acf6e46215b8fefd78bae3aa624929d
|
|
| BLAKE2b-256 |
ce140d18221df49a432dcae6e9bf79fa417f6af4fc02a0899a65b545f14adb8f
|