diff --git a/hypergraph/README.md b/hypergraph/README.md index 89bd3f4..81f585d 100644 --- a/hypergraph/README.md +++ b/hypergraph/README.md @@ -40,7 +40,7 @@ Open [http://localhost:3000](http://localhost:3000) and enter any technical topi ```bash HYPERBROWSER_API_KEY= # from hyperbrowser.ai SERPER_API_KEY= # from serper.dev — used to find source URLs -OPENAI_API_KEY= # used for graph generation with GPT-4o +ANTHROPIC_API_KEY= # used for graph generation with Claude Haiku 4.5 HYPERBROWSER_MAX_CONCURRENCY=1 # free plan: keep at 1. paid plans can increase this. ``` @@ -54,7 +54,7 @@ If you hit a concurrency limit the app will show an amber warning banner with an - **Next.js 16** — App Router, API routes - **Hyperbrowser** — scrapes source material with `onlyMainContent` for clean output -- **OpenAI GPT-4o** — generates the skill graph JSON from scraped docs +- **Claude Haiku 4.5** — generates the skill graph JSON from scraped docs - **Serper** — Google search to find the best source URLs for any topic - **react-force-graph-2d** — force-directed graph canvas rendering - **react-markdown** — renders node content in the VS Code-style preview panel diff --git a/hypergraph/components/GraphView.tsx b/hypergraph/components/GraphView.tsx index 0603dc2..48aab9d 100644 --- a/hypergraph/components/GraphView.tsx +++ b/hypergraph/components/GraphView.tsx @@ -65,6 +65,23 @@ export default function GraphView({ } }, [data]); + // Configure d3 forces via the ref (the d3Force prop is not in the TS types) + useEffect(() => { + const fg = graphRef.current; + if (!fg) return; + const charge = fg.d3Force("charge"); + if (charge) { + charge.strength(-350); + charge.distanceMax(500); + } + const link = fg.d3Force("link"); + if (link) { + link.distance((l: any) => + l.source?.type === "moc" || l.target?.type === "moc" ? 180 : 90 + ); + } + }, [data]); + const nodeCanvasObject = useCallback( (node: AnyNode, ctx: CanvasRenderingContext2D, globalScale: number) => { const type = node.type as NodeType; @@ -202,21 +219,6 @@ export default function GraphView({ d3VelocityDecay={0.2} cooldownTicks={100} onEngineStop={() => graphRef.current?.zoomToFit(400, 60)} - d3Force={(d3Force, forceName) => { - if (forceName === "charge") { - d3Force.strength(-350); // Increase repulsion (default is usually -30 or so) - d3Force.distanceMax(500); - } - if (forceName === "link") { - d3Force.distance((link: any) => { - // Push MOC nodes much further away from their children - if (link.source.type === "moc" || link.target.type === "moc") { - return 180; - } - return 90; // Standard distance for other links - }); - } - }} /> {/* Legend */} diff --git a/hypergraph/lib/generator.ts b/hypergraph/lib/generator.ts index a059817..042060b 100644 --- a/hypergraph/lib/generator.ts +++ b/hypergraph/lib/generator.ts @@ -1,7 +1,7 @@ -import OpenAI from "openai"; +import Anthropic from "@anthropic-ai/sdk"; import type { GraphNode, SkillGraph, GeneratedFile } from "@/types/graph"; -const openai = new OpenAI(); +const anthropic = new Anthropic(); const SYSTEM_PROMPT = `You are a domain knowledge graph architect. Given a topic and source material, produce a deeply interconnected JSON skill graph that enables an agent to UNDERSTAND the domain — not merely summarize it. This is the difference between an agent that follows instructions and an agent that understands a domain. @@ -61,22 +61,22 @@ export async function generateGraph( .map((d) => `## Source: ${d.url}\n\n${d.markdown.slice(0, 4000)}`) .join("\n\n---\n\n"); - const response = await openai.chat.completions.create({ - model: "gpt-4o", - response_format: { type: "json_object" }, + const response = await anthropic.messages.create({ + model: "claude-haiku-4-5-20251001", + max_tokens: 8192, + temperature: 0.7, + system: SYSTEM_PROMPT, messages: [ - { role: "system", content: SYSTEM_PROMPT }, { role: "user", - content: `Topic: ${topic}\n\nScraped documentation:\n\n${truncatedDocs}`, + content: `Topic: ${topic}\n\nScraped documentation:\n\n${truncatedDocs}\n\nRespond with ONLY the JSON object, no other text.`, }, ], - temperature: 0.7, - max_tokens: 8192, }); - const raw = response.choices[0].message.content; - if (!raw) throw new Error("Empty response from OpenAI"); + const raw = + response.content[0].type === "text" ? response.content[0].text : null; + if (!raw) throw new Error("Empty response from Claude"); const parsed = JSON.parse(raw) as SkillGraph; diff --git a/hypergraph/package-lock.json b/hypergraph/package-lock.json index b2046ff..d386d13 100644 --- a/hypergraph/package-lock.json +++ b/hypergraph/package-lock.json @@ -8,10 +8,10 @@ "name": "hypergraph", "version": "0.1.0", "dependencies": { + "@anthropic-ai/sdk": "^0.78.0", "@hyperbrowser/sdk": "^0.83.3", "jszip": "^3.10.1", "next": "16.1.6", - "openai": "^6.22.0", "react": "19.2.3", "react-dom": "19.2.3", "react-force-graph-2d": "^1.29.1", @@ -42,6 +42,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.78.0.tgz", + "integrity": "sha512-PzQhR715td/m1UaaN5hHXjYB8Gl2lF9UVhrrGrZeysiF6Rb74Wc9GCB8hzLdzmQtBd1qe89F9OptgB9Za1Ib5w==", + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "^3.1.1" + }, + "bin": { + "anthropic-ai-sdk": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -73,7 +93,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -235,6 +254,15 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", @@ -1626,7 +1654,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -1692,7 +1719,6 @@ "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/types": "8.56.0", @@ -2220,7 +2246,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2590,7 +2615,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -2996,7 +3020,6 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -3501,7 +3524,6 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3687,7 +3709,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -5137,6 +5158,19 @@ "dev": true, "license": "MIT" }, + "node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -6817,27 +6851,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/openai": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-6.22.0.tgz", - "integrity": "sha512-7Yvy17F33Bi9RutWbsaYt5hJEEJ/krRPOrwan+f9aCPuMat1WVsb2VNSII5W1EksKT6fF69TG/xj4XzodK3JZw==", - "license": "Apache-2.0", - "bin": { - "openai": "bin/cli" - }, - "peerDependencies": { - "ws": "^8.18.0", - "zod": "^3.25 || ^4.0" - }, - "peerDependenciesMeta": { - "ws": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -7118,7 +7131,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -7128,7 +7140,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -8031,7 +8042,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8078,6 +8088,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", + "license": "MIT" + }, "node_modules/ts-api-utils": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", @@ -8220,7 +8236,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8632,7 +8647,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/hypergraph/package.json b/hypergraph/package.json index 3df791d..3342680 100644 --- a/hypergraph/package.json +++ b/hypergraph/package.json @@ -9,10 +9,10 @@ "lint": "eslint" }, "dependencies": { + "@anthropic-ai/sdk": "^0.78.0", "@hyperbrowser/sdk": "^0.83.3", "jszip": "^3.10.1", "next": "16.1.6", - "openai": "^6.22.0", "react": "19.2.3", "react-dom": "19.2.3", "react-force-graph-2d": "^1.29.1",