Metadata-Version: 2.5
Name: dhrishti
Version: 0.1.11
Summary: a JSON API for live variable inspection & manipulation inside python runtimes
Project-URL: Repository, https://github.com/vedicreader/dhrishti
Project-URL: Documentation, https://vedicreader.github.io/dhrishti/
Author-email: Karthik <karthik.rajgopal@hotmail.com>
License: Apache-2.0
License-File: LICENSE
Keywords: nbdev
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Requires-Python: >=3.11
Requires-Dist: fastcore>=2.1.4
Requires-Dist: ipython>=9.15.0
Requires-Dist: numpy>=2.4.6
Requires-Dist: pandas>=3.0.3
Requires-Dist: python-fasthtml>=0.14.9
Requires-Dist: safepyrun>=0.2.5
Provides-Extra: dev
Requires-Dist: conkernelclient>=0.0.16; extra == 'dev'
Requires-Dist: fossick>=0.0.10; extra == 'dev'
Requires-Dist: koshas>=0.0.29; extra == 'dev'
Requires-Dist: nbdev>=3.3.21; extra == 'dev'
Requires-Dist: notebook>=7.6.0; extra == 'dev'
Requires-Dist: rishi>=0.0.2; extra == 'dev'
Requires-Dist: slopometer>=0.0.1; extra == 'dev'
Description-Content-Type: text/markdown

# dhrishti


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

dhrishti exposes a live Python namespace over JSON. Start it in a notebook kernel, a training script, or a web application. Clients can inspect variables, expand containers, page grids, and run code.

dhrishti has no user interface. It provides the API for an inspector or another client.

The owner namespace is the namespace of the process that runs dhrishti. An agent works in a separate sandboxed overlay. It reads owner variables and creates its own names. You can promote selected results into the owner namespace.

## Install

``` sh
pip install dhrishti
```

To install from source:

``` sh
pip install git+https://github.com/vedicreader/dhrishti.git
```

## What it shows

A namespace maps names to values. [`snapshot`](https://vedicreader.github.io/dhrishti/core.html#snapshot) returns inspector rows with a name, type, value, and shape.

``` python
import pandas as pd
from dhrishti.core import snapshot, expand, grid_page
```

``` python
ns = dict(x=42, names=['ada','turing','hopper'], df=pd.DataFrame({'a':[1,2,3], 'b':[4,5,6]}))
for v in snapshot(ns): print(f'{v.name} = {v.value}  ({v.type})')
```

    df = DataFrame [3×2]  (DataFrame)
    names = ['ada', 'turing', 'hopper']  (list)
    x = 42  (int)

[`expand`](https://vedicreader.github.io/dhrishti/core.html#expand) returns one level of a container. A DataFrame expands into metadata and columns. [`grid_page`](https://vedicreader.github.io/dhrishti/core.html#grid_page) pages NumPy and pandas data.

``` python
for v in expand(ns, ('df',)): print(f'{v.name}: {v.value}')
```

    shape: (3, 2)
    size: 6
    dtypes: Series [2] object
    a: Series [3] int64
    b: Series [3] int64

``` python
grid_page(ns, ('df',))['cells']
```

    [['1', '4'], ['2', '5'], ['3', '6']]

## Serving it

[`serve_in_kernel()`](https://vedicreader.github.io/dhrishti/serving.html#serve_in_kernel) starts an API server in a live IPython kernel. It returns the server port.

[`serve()`](https://vedicreader.github.io/dhrishti/serving.html#serve) starts an API server for a plain process. It uses the caller’s globals by default. It returns the base URL.

Shell messages wait for the running cell. The HTTP server runs on its own thread and continues to answer requests while the cell runs.

[`serve`](https://vedicreader.github.io/dhrishti/serving.html#serve) starts the JSON API on a background thread and returns the server and its base URL. It serves the globals of the caller unless you pass `ns`. [`active`](https://vedicreader.github.io/dhrishti/registry.html#active) lists the running dhrishti servers.

``` python
from dhrishti.serving import serve, owner_token, active
import httpx2 as httpx
```

``` python
x, names = 42, ['ada', 'turing', 'hopper']
server, url = serve(name='readme')
rows = httpx.get(f'{url}/api/rows').json()
[n['name'] for g in rows['groups'] for n in g['nodes']]
```

<script>
document.body.addEventListener('htmx:configRequest', (event) => {
    if(event.detail.path.includes('://')) return;
    htmx.config.selfRequestsOnly=false;
    event.detail.path = `${location.protocol}//${location.hostname}:8000${event.detail.path}`;
});
</script>

    ['url',
     'server',
     'names',
     'x',
     'v',
     'ns',
     'active',
     'owner_token',
     'serve',
     'grid_page',
     'expand',
     'snapshot',
     'set_logging',
     'open']

### The endpoints

Accessors are JSON-encoded tuples. `["df", "x", 0]` addresses `df['x'][0]`. The same format addresses namespace bindings, container children, DataFrame columns, and array cells.

| endpoint | what it gives you |
|----|----|
| `GET /api/rows?profile=&sort=` | the grouped node tree; `sort` is `name`\|`recent`\|`type`\|`size` |
| `GET /api/expand?accessor=&offset=` | one level of children, paged |
| `GET /api/grid?accessor=&roff=&coff=` | a windowed grid for anything numpy- or pandas-backed |
| `GET /api/result` | the last expression, kept at the `_` accessor so you can expand into it |
| `GET /api/history?n=` | the rolling exec-history notebook’s code cells |
| `GET /api/sessions`, `/api/session?name=` | logged notebooks, and one by stem |
| `GET /api/envs` | this env’s label plus every other live dhrishti process |
| `GET /api/exec?code=&scope=` | run code. `global` mutates and `isolated` uses a copy |
| `GET /api/set?accessor=&expr=` | assign an expression at an accessor |
| `GET /api/promote?accessor=` | adopt an agent’s variable into the owner namespace |

`/api/exec`, `/api/set`, and `/api/promote` require an owner token. The server writes the token to `<reg_dir>/token-<port>` with mode 0600. A local client sends it in `X-Dhrishti-Token` or `?token=`. Agents never receive the token.

The token controls access between local tools. It is not a cryptographic boundary. The API binds to localhost. Anyone who can read the token file can use the token.

[`serve()`](https://vedicreader.github.io/dhrishti/serving.html#serve) writes a JSON entry to the registry directory. [`active()`](https://vedicreader.github.io/dhrishti/registry.html#active) returns live entries and removes entries for dead processes.

`/api/rows`, `/api/expand` and `/api/grid` read the namespace. `/api/exec`, `/api/set` and `/api/promote` change it. These three need the owner token that [`owner_token`](https://vedicreader.github.io/dhrishti/serving.html#owner_token) returns. A server started with `token=False` does not check the token.

``` python
tok = owner_token()
httpx.get(f'{url}/api/exec', params={'code': 'y = x * 2', 'token': tok}).json()['ok']
print('y is now', y)
[(e['name'], e['port']) for e in active()]
```

    y is now 84

    [('readme', 8000)]

## Handing your namespace to an agent

Start a server with the agent API enabled. This example uses [rishi](https://github.com/vedicreader/rishi) to run a local model. The [agent page](https://vedicreader.github.io/dhrishti/agent.html) has the complete example.

``` python
from dhrishti.serving import agent_session, AGENT_POLICY
from dhrishti.agent import agent_tools, run_coro, AgentSession
```

``` python
df = pd.DataFrame({'x': range(5), 'y': list('abcde')})
sess = agent_session()
print(AGENT_POLICY[:120], '...')
```

    agent access: persistent session defined by Sandboxed Python session over a shared owner namespace. You can read and acc ...

`agent_tools(sess)` provides `list_vars` and `run_python` for one session. `conversation_logger(sess)` writes each turn to a transcript notebook. Pass both to `Chat`.

``` python
from fastcore.docments import docstring
```

``` python
sp = f'You are a Python assistant. you have a persistent session defined by {docstring(AgentSession)} Use builtins where possible. import only once. dont run same code again. check globals check if a module before using it. numpy and pandas are always be avilable. Call list_vars before writing code. tools available: {docstring(agent_tools)}'; sp
```

    'You are a Python assistant. you have a persistent session defined by Sandboxed Python session over a shared owner namespace. You can read and access any owner variable freely; your writes land in your own layer and never touch the owner. Mutating or deleting owner variables is blocked. copy to a new name first (e.g. `df2 = df.copy()`, `lst2 = lst[:]`). Check available variables before writing code. Use builtins where possible. import only once. dont run same code again. check globals check if a module before using it. numpy and pandas are always be avilable. Call list_vars before writing code. tools available: Tools for a tool-calling agent over an AgentSession. `list_vars` shows what is available; call it first. `run_python` executes code in the session.'

``` python
from rishi import *
from rishi.litert import gemma4_e4b
from litert_lm import set_min_log_severity, Backend
from dhrishti.agent import conversation_logger
from fastcore.docments import docstring
set_min_log_severity(5)

chat = Chat(model=gemma4_e4b, sp=sp, tools=agent_tools(sess), cbs=[conversation_logger(sess)], backend=Backend.GPU(),
            approve=hitl_policy({'list_vars':'approved', 'run_python':'approved'}))
chat('Scale the numeric columns of df to 0..1 as a new frame df_norm.')
```

The numeric columns of `df` have been successfully scaled to the range of 0..1 and the result is stored in the new frame `df_norm`. The output shows the first few rows of `df_norm` with the normalized values.

``` python
sess.ns['df_norm']
```

<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }
&#10;    .dataframe tbody tr th {
        vertical-align: top;
    }
&#10;    .dataframe thead th {
        text-align: right;
    }
</style>

|     | x    | y   |
|-----|------|-----|
| 0   | 0.00 | a   |
| 1   | 0.25 | b   |
| 2   | 0.50 | c   |
| 3   | 0.75 | d   |
| 4   | 1.00 | e   |

</div>

Agents that use HTTP call `/agent/api/*`. The endpoints return the same rows and grids as the owner endpoints. The overlay and the restriction list apply to every request.

`agent='restricted'` uses the persistent overlay. `agent='readonly'` runs every cell in an isolated sandbox. `agent='off'` refuses all agent endpoints.

`/agent/api/sessions` lists every saved agent transcript, newest first, and names the live session. `/agent/api/transcript` returns one transcript. It returns the live session when you pass no `session`.

## What the agent can’t do

The agent reads owner variables. It writes only to its overlay. The session refuses changes to owner variables, file deletion and moves, and shell commands. A refused cell returns the reason in place of a result.

``` python
print(sess.run("df['z'] = 0").error)
print(sess.run("import shutil; shutil.rmtree('/nope')").error)
list(df.columns)
```

    blocked: cannot modify owner variable 'df' in place; bind your result to a new name instead
    blocked: 'shutil.rmtree' is a shell/filesystem escape and is not allowed in the agent session; use your own shell tool

    ['x', 'y']

## Promote what you want to keep

`/agent/api/rows` lists names in the agent overlay. `/api/promote` binds one agent name in the owner namespace. Promotion keeps the same object reference. It requires the owner token.

`/api/promote` puts a top-level variable of the agent overlay into the owner namespace. Both names then refer to one object. The call needs the owner token.

``` python
import json as _json
```

``` python
sess.run('df_norm = df.assign(x_norm=(df.x - df.x.min()) / (df.x.max() - df.x.min()))')
print(httpx.get(f'{url}/agent/api/rows').json()['agent_names'])
httpx.get(f'{url}/api/promote', params={'accessor': _json.dumps(['df_norm']), 'token': tok}).json()
```

    ['col', 'df_norm', 'max_val', 'min_val', 'np', 'numeric_cols']

    {'ok': True, 'error': None}

``` python
'df_norm' in globals()
```

    True

## Learn more

The [documentation](https://vedicreader.github.io/dhrishti/) covers the inspection core, runtime, serving layer, and agent overlay.

Dhrishti uses [nbdev](https://nbdev.fast.ai). Edit notebooks under `nbs/`. Run `nbdev-prepare` to export the library, run tests, and rebuild documentation.
