Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.2
hooks:
- id: ruff-check
args: [--fix]
- id: ruff-format

- repo: https://github.com/hoxbro/ty-pre-commit
rev: v0.0.17
hooks:
- id: ty-check
args: [--ignore, unresolved-import]
24 changes: 24 additions & 0 deletions viewer_new/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
19 changes: 19 additions & 0 deletions viewer_new/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# `viewer_new`

Standalone Bun + Vite + React viewer for DeepRetro.

## Commands

```bash
bun install
bun run dev
bun run build
bun run typecheck
bun run test
```

## Notes

- The legacy `viewer/` app is intentionally untouched.
- `bun run sync:config` copies the root `config/advanced_settings.json` into `public/advanced-settings.json`.
- Runtime backend configuration lives in `public/runtime-config.json`.
766 changes: 766 additions & 0 deletions viewer_new/bun.lock

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions viewer_new/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'

export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
},
])
12 changes: 12 additions & 0 deletions viewer_new/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DeepRetro Viewer</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
51 changes: 51 additions & 0 deletions viewer_new/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"name": "viewer_new",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"sync:config": "node ./scripts/sync-public-config.ts",
"predev": "node ./scripts/sync-public-config.ts",
"prebuild": "node ./scripts/sync-public-config.ts",
"pretest": "node ./scripts/sync-public-config.ts",
"pretypecheck": "node ./scripts/sync-public-config.ts",
"dev": "vite",
"build": "tsc -b && vite build",
"typecheck": "tsc -b --pretty false",
"test": "vitest run",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@codemirror/lang-json": "^6.0.2",
"@rdkit/rdkit": "^2025.3.4-1.0.0",
"@tanstack/react-query": "^5.90.21",
"@uiw/react-codemirror": "^4.25.7",
"@xyflow/react": "^12.10.1",
"elkjs": "^0.11.1",
"lucide-react": "^0.577.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"zod": "^4.3.6",
"zustand": "^5.0.11"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
"@types/node": "^25.3.5",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"jsdom": "^28.1.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.48.0",
"vite": "^7.3.1",
"vitest": "^4.0.18"
}
}
59 changes: 59 additions & 0 deletions viewer_new/public/advanced-settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"llm_models": {
"claude3": {
"internal_name": "claude-3-opus-20240229",
"display_name": "Claude 3 Opus",
"supports_advanced_prompt": true,
"supports_stability_check": true,
"supports_hallucination_check": true,
"supports_protecting_group_feature": false
},
"claude37": {
"internal_name": "anthropic/claude-3-7-sonnet-20250219",
"display_name": "Claude 3.7 Sonnet",
"supports_advanced_prompt": true,
"supports_stability_check": true,
"supports_hallucination_check": true,
"supports_protecting_group_feature": false
},
"deepseek": {
"internal_name": "fireworks_ai/accounts/fireworks/models/deepseek-r1",
"display_name": "DeepSeek",
"supports_advanced_prompt": true,
"supports_stability_check": true,
"supports_hallucination_check": true,
"supports_protecting_group_feature": false
},
"claude4opus": {
"internal_name": "anthropic/claude-opus-4-20250514",
"display_name": "Claude 4 Opus",
"supports_advanced_prompt": true,
"supports_stability_check": true,
"supports_hallucination_check": true,
"supports_protecting_group_feature": false
},
"claude4sonnet": {
"internal_name": "anthropic/claude-sonnet-4-20250514",
"display_name": "Claude 4 Sonnet",
"supports_advanced_prompt": true,
"supports_stability_check": true,
"supports_hallucination_check": true,
"supports_protecting_group_feature": false
}
},
"az_models": {
"USPTO": { "display_name": "USPTO" },
"Pistachio_25": { "display_name": "Pistachio (25)" },
"Pistachio_50": { "display_name": "Pistachio (50)" },
"Pistachio_100": { "display_name": "Pistachio (100)" },
"Pistachio_100+": { "display_name": "Pistachio (100+)" }
},
"defaults": {
"model_type": "claude4sonnet",
"advanced_prompt": false,
"model_version": "USPTO",
"stability_flag": true,
"hallucination_check": true,
"use_protecting_group_feature": false
}
}
24 changes: 24 additions & 0 deletions viewer_new/public/runtime-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"instances": [
{
"id": "instance1",
"label": "Pathway 1",
"baseUrl": "http://localhost:5000",
"defaults": {
"model_type": "claude4sonnet",
"advanced_prompt": true,
"model_version": "Pistachio_100+",
"stability_flag": true,
"hallucination_check": true,
"use_protecting_group_feature": false
}
}
],
"endpoints": {
"retrosynthesis": "/api/retrosynthesis",
"rerun": "/api/rerun_retrosynthesis",
"partialRerun": "/api/partial_rerun",
"saveEdited": "/api/save_edited_result",
"health": "/api/health"
}
}
12 changes: 12 additions & 0 deletions viewer_new/scripts/sync-public-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { copyFile, mkdir } from "node:fs/promises";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const scriptDir = dirname(fileURLToPath(import.meta.url));
const source = resolve(scriptDir, "../../config/advanced_settings.json");
const target = resolve(scriptDir, "../public/advanced-settings.json");

await mkdir(dirname(target), { recursive: true });
await copyFile(source, target);

console.log(`Synced advanced settings to ${target}`);
77 changes: 77 additions & 0 deletions viewer_new/src/api/flaskClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import type {
AnalysisRequest,
PathwayResult,
ViewerRuntimeConfig,
} from "../types/viewer";

type InstanceEndpointConfig = {
baseUrl: string;
endpoints: ViewerRuntimeConfig["endpoints"];
apiKey: string;
};

async function requestJson<T>(
url: string,
init: RequestInit,
): Promise<T> {
const response = await fetch(url, init);
const data = (await response.json().catch(() => ({}))) as T & {
error?: string;
};

if (!response.ok) {
throw new Error(data.error || `Request failed with ${response.status}`);
}

return data;
}

function withHeaders(apiKey: string) {
return {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
};
}

export function createFlaskClient(config: InstanceEndpointConfig) {
const buildUrl = (path: string) => `${config.baseUrl}${path}`;

return {
async retrosynthesis(body: AnalysisRequest) {
return requestJson<PathwayResult>(buildUrl(config.endpoints.retrosynthesis), {
method: "POST",
headers: withHeaders(config.apiKey),
body: JSON.stringify(body),
});
},
async rerun(body: AnalysisRequest) {
return requestJson<PathwayResult>(buildUrl(config.endpoints.rerun), {
method: "POST",
headers: withHeaders(config.apiKey),
body: JSON.stringify(body),
});
},
async partialRerun(body: AnalysisRequest & { steps: string }) {
return requestJson<PathwayResult>(buildUrl(config.endpoints.partialRerun), {
method: "POST",
headers: withHeaders(config.apiKey),
body: JSON.stringify(body),
});
},
async saveEditedResult(smiles: string, result: PathwayResult) {
return requestJson<{ message: string }>(buildUrl(config.endpoints.saveEdited), {
method: "POST",
headers: withHeaders(config.apiKey),
body: JSON.stringify({ smiles, result }),
});
},
async health() {
return requestJson<{ status: string }>(buildUrl(config.endpoints.health), {
method: "GET",
headers: {
"X-API-KEY": config.apiKey,
},
});
},
};
}
Loading