-
Notifications
You must be signed in to change notification settings - Fork 9
0.8 #142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
0.8 #142
Changes from all commits
d868b53
dd99cdb
2e764ca
1b20cab
06fb6c6
4508bdb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,54 +1,33 @@ | ||
| from fastapi import APIRouter, Depends | ||
| from pydantic import BaseModel | ||
| from ..state import AppState, get_state | ||
| from ..auth import require_user_email | ||
|
|
||
| from ..data_models import NDIFResponse | ||
|
|
||
| from nnsightful.types import JLensData | ||
| from nnsightful.tools.j_lens import j_lens | ||
|
|
||
| from ..auth import require_user_email | ||
| from ..sse import stream_tool | ||
| from ..state import AppState, get_state | ||
|
|
||
| router = APIRouter() | ||
|
|
||
|
|
||
| class JLensRequest(BaseModel): | ||
| model: str | ||
| prompt: str | ||
| topk: int = 5 # Number of top-k predictions per cell | ||
| include_entropy: bool = True # Whether to include entropy data | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check whether the j_lens tool accepts include_entropy, and whether the frontend sends it.
fd -t f 'j_lens.py' | xargs -r ast-grep outline --items all
rg -n -C3 'include_entropy' --glob '*.py' --glob '*.ts'Repository: ndif-team/workbench Length of output: 570 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- j_lens route ---'
cat -n workbench/_api/routes/j_lens.py
printf '%s\n' '--- matching route/tool files ---'
fd -t f -i 'logit_lens.py|j_lens.py' .
printf '%s\n' '--- include_entropy references ---'
rg -n -C4 'include_entropy' .
printf '%s\n' '--- stream_tool call sites ---'
rg -n -C5 'stream_tool' workbench/_api/routesRepository: ndif-team/workbench Length of output: 16923 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- j_lens definitions and references ---'
rg -n -C6 'def j_lens|async def j_lens|j_lens\(' . --glob '*.py'
printf '%s\n' '--- dependency declarations ---'
rg -n -C3 'nnsightful' . --glob 'pyproject.toml' --glob 'requirements*.txt' --glob 'poetry.lock' --glob 'uv.lock' --glob '*.py'
printf '%s\n' '--- route and frontend API context ---'
cat -n workbench/_api/routes/logit_lens.py
cat -n workbench/_web/src/lib/api/jlensApi.tsRepository: ndif-team/workbench Length of output: 14807 🏁 Script executed: #!/bin/bash
set -e
base='https://raw.githubusercontent.com/AdamBelfki3/nnsightful/75e42670b64f2aa5fcb82421e910376368c934d3'
for path in nnsightful/tools/j_lens.py nnsightful/tools/logit_lens.py; do
printf '\n--- %s ---\n' "$path"
curl --fail --silent --show-error "$base/$path" | nl -ba | sed -n '1,240p'
doneRepository: ndif-team/workbench Length of output: 403 🏁 Script executed: #!/bin/bash
set -e
curl --fail --silent --show-error \
'https://api.github.com/repos/AdamBelfki3/nnsightful/git/trees/75e42670b64f2aa5fcb82421e910376368c934d3?recursive=1' |
grep -o '"path": "[^"]*"' |
grep -E 'j_lens|logit_lens'Repository: ndif-team/workbench Length of output: 278 🏁 Script executed: #!/bin/bash
set -e
base='https://raw.githubusercontent.com/AdamBelfki3/nnsightful/75e42670b64f2aa5fcb82421e910376368c934d3'
for path in src/nnsightful/tools/j_lens.py src/nnsightful/tools/logit_lens.py; do
printf '\n--- %s ---\n' "$path"
curl --fail --silent --show-error "$base/$path" | sed -n '1,260p'
doneRepository: ndif-team/workbench Length of output: 14381 🏁 Script executed: #!/bin/bash
set -e
python3 - <<'PY'
import ast
import json
import urllib.request
from pathlib import Path
commit = "75e42670b64f2aa5fcb82421e910376368c934d3"
url = (
"https://raw.githubusercontent.com/AdamBelfki3/nnsightful/"
f"{commit}/src/nnsightful/tools/j_lens.py"
)
with urllib.request.urlopen(url) as response:
tool_source = response.read().decode()
tool_tree = ast.parse(tool_source)
format_fn = next(
node for node in ast.walk(tool_tree)
if isinstance(node, ast.FunctionDef) and node.name == "_format"
)
run_fn = next(
node for node in ast.walk(tool_tree)
if isinstance(node, ast.FunctionDef) and node.name == "_run"
)
format_args = {arg.arg for arg in format_fn.args.args}
format_has_entropy_branch = any(
isinstance(node, ast.If)
and isinstance(node.test, ast.Name)
and node.test.id == "include_entropy"
for node in ast.walk(format_fn)
)
run_forwards_kwargs = any(
isinstance(node, ast.Call)
and isinstance(node.func, ast.Attribute)
and node.func.attr == "_format"
and any(keyword.arg is None for keyword in node.keywords)
for node in ast.walk(run_fn)
)
route_source = Path("workbench/_api/routes/j_lens.py").read_text()
route_tree = ast.parse(route_source)
route_call = next(
node for node in ast.walk(route_tree)
if isinstance(node, ast.Call)
and isinstance(node.func, ast.Name)
and node.func.id == "stream_tool"
)
route_keywords = {keyword.arg for keyword in route_call.keywords}
print(json.dumps({
"pinned_commit": commit,
"j_lens_format_accepts_include_entropy": "include_entropy" in format_args,
"j_lens_format_uses_include_entropy": format_has_entropy_branch,
"j_lens_run_forwards_format_kwargs": run_forwards_kwargs,
"route_forwards_include_entropy": "include_entropy" in route_keywords,
}, indent=2))
PYRepository: ndif-team/workbench Length of output: 2444 🏁 Script executed: #!/bin/bash
set -e
python3 - <<'PY'
import ast
import json
import subprocess
from pathlib import Path
commit = "75e42670b64f2aa5fcb82421e910376368c934d3"
url = (
"https://raw.githubusercontent.com/AdamBelfki3/nnsightful/"
f"{commit}/src/nnsightful/tools/j_lens.py"
)
tool_source = subprocess.check_output(
["curl", "--fail", "--silent", "--show-error", url],
text=True,
)
tree = ast.parse(tool_source)
format_fn = next(
node for node in ast.walk(tree)
if isinstance(node, ast.FunctionDef) and node.name == "_format"
)
run_fn = next(
node for node in ast.walk(tree)
if isinstance(node, ast.FunctionDef) and node.name == "_run"
)
format_args = {arg.arg for arg in format_fn.args.args}
format_has_entropy_branch = any(
isinstance(node, ast.If)
and isinstance(node.test, ast.Name)
and node.test.id == "include_entropy"
for node in ast.walk(format_fn)
)
run_forwards_kwargs = any(
isinstance(node, ast.Call)
and isinstance(node.func, ast.Attribute)
and node.func.attr == "_format"
and any(keyword.arg is None for keyword in node.keywords)
for node in ast.walk(run_fn)
)
route_tree = ast.parse(Path("workbench/_api/routes/j_lens.py").read_text())
route_call = next(
node for node in ast.walk(route_tree)
if isinstance(node, ast.Call)
and isinstance(node.func, ast.Name)
and node.func.id == "stream_tool"
)
route_keywords = {keyword.arg for keyword in route_call.keywords}
print(json.dumps({
"pinned_commit": commit,
"j_lens_format_accepts_include_entropy": "include_entropy" in format_args,
"j_lens_format_uses_include_entropy": format_has_entropy_branch,
"j_lens_run_forwards_format_kwargs": run_forwards_kwargs,
"route_forwards_include_entropy": "include_entropy" in route_keywords,
}, indent=2))
PYRepository: ndif-team/workbench Length of output: 406 Forward 🤖 Prompt for AI Agents |
||
|
|
||
|
|
||
| class JLensResponse(NDIFResponse): | ||
| data: JLensData | None = None | ||
|
|
||
|
|
||
| @router.post("/start", response_model=JLensResponse) | ||
| async def start_j_lens( | ||
| @router.post("/run") | ||
| async def run_j_lens( | ||
| req: JLensRequest, | ||
| state: AppState = Depends(get_state), | ||
| user_email: str = Depends(require_user_email), | ||
| ): | ||
| model = state[req.model] | ||
| backend = state.make_backend(model=model) | ||
|
|
||
| output = j_lens._run(model, req.prompt, remote=state.remote, backend=backend, non_blocking=state.remote, raw=False, top_k=req.topk) | ||
|
|
||
| if not backend.blocking: | ||
| return {"job_id": output} | ||
|
|
||
|
|
||
| return {"data": j_lens.to_data_obj(**output)} | ||
|
|
||
|
|
||
| @router.post("/results/{job_id}", response_model=JLensResponse) | ||
| async def collect_j_lens( | ||
| job_id: str, | ||
| req: JLensRequest, | ||
| state: AppState = Depends(get_state), | ||
| user_email: str = Depends(require_user_email), | ||
| ): | ||
| backend = state.make_backend(job_id=job_id) | ||
| results = backend()['results'] | ||
|
|
||
| data = j_lens.to_data_obj(**results) | ||
|
|
||
| return {"data": data} | ||
| """Run the Jacobian lens, streaming status until the data lands (see ``sse``).""" | ||
| return stream_tool( | ||
| state, | ||
| j_lens, | ||
| state[req.model], | ||
| req.prompt, | ||
| top_k=req.topk, | ||
| ) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Enforce model access on every streamed execution path.
These routes require only the caller identity and do not call
require_model_accessbefore resolving or streaming the requested model. A caller without access can therefore run gated remote models through activation patching, causal mediation, j-lens, and logit-lens endpoints.Call
require_model_access(state, user_email, model_name)before model resolution orstream_toolin each route so denied requests return a 403 before streaming begins.📍 Affects 2 files
workbench/_api/routes/activation_patching.py#L25-L35(this comment)workbench/_api/sse.py#L95-L126🤖 Prompt for AI Agents