CodeNSM
[ DEVELOPER DOCS ]

CodeNSM Python SDK

Treat every Python function like an employee. The SDK auto-instruments the functions and methods in your packages — no decorating each one — and tracks how often each runs, how reliably, and how slowly. Those signals roll up, in the cloud, into departments, KPIs and a single North-Star Metric on your dashboard.

1 · Install

pip install https://thinknorth.consulting/static/codensm/codensm-0.3.0-py3-none-any.whl

Hosted build (interim). pip install codensm from PyPI is coming. Pure standard library — no third-party dependencies pulled into your app. Python 3.8+. New in 0.3.0: mode="full" — optional local static analysis that unlocks flow diagrams, complexity signals and the IP split (details below). From 0.2.0: code-location reporting (file/line, once per function), a 4,000-function buffer guardrail, and automatic back-off on plan-quota 429s.

Previous build: codensm-0.2.0-py3-none-any.whl remains hosted at the same path if you need to pin it.

2 · Quickstart

Call init() once, as early as possible at startup — before your app's submodules import, so the import hook can wrap them.

import codensm

codensm.init(
    api_key="cnsm_…",                  # Project → ingest key, from your dashboard
    include=["myapp"],                 # your own top-level packages
    weights={"myapp.checkout": 9},   # optional business value-weights
)

That's it. Every function/method under myapp — already imported or imported later — is now tracked. Nothing else to wire.

3 · Configuration

OptionDefaultWhat it does
api_keyRequired. Your project's ingest key (Bearer token).
includeRequired. Package prefixes to auto-instrument, e.g. ["myapp"].
endpoint…/codensm/ingestIngest URL. Defaults to hosted CodeNSM.
exclude()Prefixes to skip even if under include (hot inner loops, etc.).
departmentautocallable(module)->str or {prefix: dept}. Defaults to the package path.
weights1.0{name-or-prefix: value_weight} — business value per successful call (longest-prefix match).
flush_interval15.0Seconds between background flushes.
instrumentTrueSet False to only enable @track (no auto-wrap).
mode"names""names" sends function names + counters only. "full" adds local control-flow analysis — see full-code insight.

4 · Overriding or opting in one function

Use the decorator only to override a function's department/weight, or to instrument something outside include.

@codensm.track(department="payments", value_weight=9)
def charge(card): ...

5 · Short-lived scripts

The flusher runs in the background and on exit; force it when a script ends fast:

codensm.flush()      # send now
codensm.shutdown()   # final send + stop

6 · What leaves your process (and what never does)

CodeNSM is deliberately a thin collector. The interpretation happens in the cloud — your machine ships only lightweight counters.

Sent

function namemodule.qualname
call countinteger
error countinteger
total latencymilliseconds
file + linerelative path, sent once
your department/weightconfig you set

Never sent

arguments / return values
source code
request/response bodies
PII / payloads
stack contents

Wrappers never swallow exceptions or change return values — your behaviour is unchanged. Counters live in memory; a daemon thread batches deltas every flush_interval seconds; a failed send is retried (merged back, never dropped).

7 · Full-code insight — mode="full" (new in 0.3.0)

Pass mode="full" to init() and the SDK also runs static analysis on your machine, using only the standard-library ast module. It parses each of your modules as it imports and extracts the control-flow shape of every function — never the source itself:

Analysed locallyWhat ships
Control-flow graphNode/edge shape only — block, decision, loop, end/error node kinds with generic labels.
Complexity signalsDecision, loop and return counts; node count; line count.
Call edgescalls_out — names of functions called, filtered to your own include packages (max 40).
Test scanA background scan of your test files for each function name — a has_tests verdict, nothing else.

Flow facts ride along with the normal counter flush as a flows array:

{"flows":[
  {"name":"myapp.pay.charge",
   "nodes":[{"type":"start"},{"type":"decision"},{"type":"block"},{"type":"end"}],
   "edges":[[0,1],[1,2],[2,3]],
   "decisions":1,"loops":0,"returns":1,"nodes_count":4,"lines":18,
   "calls_out":["myapp.pay.validate","log_event"],
   "has_tests":true}
]}

No argument values, no literals, no statements, no source text — the payload above is the complete shape of what full mode adds. Your source never leaves your machine.

mode="names" (default)mode="full"
capturedFunction names + counters (calls, errors, latency).Names + counters, plus control-flow shape, call edges, has-tests.
unlockedOffice, live states, NSM dashboard, employees table, debt trajectories, AI fix prompts.All of the left, plus BPMN-style flow diagrams, complexity signals, the unique-IP vs boilerplate split, and test-coverage reads.
overheadOne thin wrapper per call.Same, plus a one-off ast parse per module at import and one background test scan.

8 · Quotas, limits & back-off

Plans meter tracked calls per calendar month (Free 100k · Team 5M · Scale 50M — see pricing). The ingest API enforces:

LimitBehaviour
512 KB body / 2,000 functions per payloadHTTP 413 with a clear error — flush more often or scope include tighter.
100–120% of monthly quotaAccepted, month flagged as overage — a spike never blinds you mid-incident.
>120% of monthly quotaHTTP 429 {"error":"quota_exceeded"}. Clients should back off.

The Python SDK handles this for you: on a 429 it merges the batch back (nothing is lost) and doubles its flush interval, up to 5 minutes, for the rest of the session. It also caps its in-process buffer at 4,000 distinct functions — beyond that, brand-new names are dropped and counted (reported as "_meta":{"dropped":n}, which the server accepts and ignores).

9 · Any other language

The SDK is just a client for one HTTP contract. Push per-function aggregate deltas from anything:

POST https://thinknorth.consulting/codensm/ingest
Authorization: Bearer <project ingest key>
Content-Type: application/json

{"functions":[
  {"name":"checkout.apply_coupon","department":"engine",
   "value_weight":9,"calls":1240,"errors":3,"total_ms":47000,
   "file":"checkout/coupons.py","line":42}
]}

file/line are optional and stored once (set-if-empty) — they power the file guess inside AI fix prompts and the GitHub commit mapping.

10 · Where it shows up

Calls, errors and latency roll up — in the cloud — into per-department KPIs, a debt-that-hurts heatmap, underuse detection, and a single North-Star Metric, all on your project dashboard. Functions appear as they're actually called.

11 · Overhead

One thin wrapper call per instrumented call. Scope include tightly, use exclude for very hot inner loops, and remember dunder methods are skipped. Sync, async def, and instance/class/static methods are all covered.