Skip to content

Commit b941a23

Browse files
nvidiaclaude
authored andcommitted
Add ERC-8021 builder codes, fix LLM JSON parsing, context truncation
- base-chain.ts: new module for Base chain ERC-8021 builder code attribution — records paper authors on Base transactions - content-generator.ts: robust JSON extraction handling think tags, code fences, and bracket scanning; truncate paper context to 24K chars to prevent 413 payload errors - lesson-watcher.ts: integrate Base chain attribution after publishing - server.ts: optional BaseChain init from BASE_RPC_URL env var - Add ethers dependency for Base chain transactions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ee29110 commit b941a23

7 files changed

Lines changed: 329 additions & 24 deletions

File tree

cogito/.env.example

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1-
# Required: Anthropic API key for Claude Code
2-
ANTHROPIC_API_KEY=sk-ant-...
3-
4-
# AIN blockchain node (inside docker network)
1+
# AIN blockchain node
52
AIN_PROVIDER_URL=http://ain-blockchain:8080
63

7-
# AIN wallet private key (same key as the node)
4+
# AIN wallet private key
85
AIN_PRIVATE_KEY=b22c95ffc4a5c096f7d7d0487ba963ce6ac945bdc91c79b64ce209de289bec96
96

10-
# Base chain (for ERC-8004/8021 transactions)
11-
BASE_RPC_URL=https://mainnet.base.org
12-
BASE_PRIVATE_KEY=${AIN_PRIVATE_KEY}
7+
# vLLM endpoint (local LLM for content generation)
8+
VLLM_URL=http://vllm:8000
9+
VLLM_MODEL=Qwen/Qwen3-32B-AWQ
10+
11+
# Lesson watcher
12+
POLL_INTERVAL_MS=30000
13+
CONTENT_PRICE=0.005
14+
15+
# x402 server port
16+
X402_PORT=3402
1317

14-
# Exploration settings
15-
THINK_INTERVAL_MS=120000
16-
MAX_PAPERS_PER_CYCLE=5
18+
# Base chain — optional, enables ERC-8021 builder code attribution
19+
# BASE_RPC_URL=https://mainnet.base.org
20+
# BASE_PRIVATE_KEY=your_base_private_key

cogito/package-lock.json

Lines changed: 108 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cogito/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@
1111
},
1212
"dependencies": {
1313
"@ainblockchain/ain-js": "^1.14.0",
14-
"dotenv": "^16.4.0"
14+
"dotenv": "^16.4.0",
15+
"ethers": "^6.16.0"
1516
},
1617
"devDependencies": {
17-
"typescript": "^5.4.0",
1818
"@types/node": "^22.0.0",
19-
"tsx": "^4.7.0"
19+
"tsx": "^4.7.0",
20+
"typescript": "^5.4.0"
2021
}
2122
}

cogito/src/base-chain.ts

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/**
2+
* Base chain integration for the Cogito container.
3+
* ERC-8021 builder codes attribute paper authors on Base transactions.
4+
*
5+
* Only active when BASE_RPC_URL and BASE_PRIVATE_KEY are set.
6+
*/
7+
8+
import { ethers } from 'ethers';
9+
import { Paper } from './paper-discovery.js';
10+
11+
// ERC-8021 constants
12+
const ERC_MARKER = '80218021802180218021802180218021';
13+
const SCHEMA_ID = '00';
14+
const COGITO_BUILDER_CODE = 'cogito_node';
15+
16+
interface AuthorAttribution {
17+
type: 'arxiv' | 'github' | 'doi' | 'custom';
18+
identifier: string;
19+
}
20+
21+
/**
22+
* Encode builder codes into an ERC-8021 hex suffix.
23+
*/
24+
function encodeBuilderCodes(codes: string[]): string {
25+
if (codes.length === 0) throw new Error('At least one builder code is required');
26+
27+
const codesString = codes.join(',');
28+
const codesBytes = Buffer.from(codesString, 'ascii');
29+
30+
if (codesBytes.length > 255) {
31+
throw new Error(`Builder codes total length ${codesBytes.length} exceeds 255 bytes`);
32+
}
33+
34+
const codesLengthHex = codesBytes.length.toString(16).padStart(2, '0');
35+
const codesHex = codesBytes.toString('hex');
36+
37+
return codesLengthHex + codesHex + SCHEMA_ID + ERC_MARKER;
38+
}
39+
40+
/**
41+
* Build author attributions from paper metadata.
42+
*/
43+
function buildAuthorCodes(paper: Paper): AuthorAttribution[] {
44+
const attributions: AuthorAttribution[] = [];
45+
46+
if (paper.arxivId) {
47+
attributions.push({ type: 'arxiv', identifier: paper.arxivId });
48+
}
49+
50+
if (paper.codeUrl) {
51+
// Extract owner/repo from GitHub URL
52+
const match = paper.codeUrl.match(/github\.com\/([^/]+\/[^/]+)/);
53+
if (match) {
54+
attributions.push({ type: 'github', identifier: match[1] });
55+
}
56+
}
57+
58+
// First author's last name
59+
if (paper.authors.length > 0) {
60+
const lastName = paper.authors[0].split(' ').pop()?.toLowerCase();
61+
if (lastName) {
62+
attributions.push({ type: 'custom', identifier: lastName });
63+
}
64+
}
65+
66+
return attributions;
67+
}
68+
69+
export class BaseChain {
70+
private provider: ethers.JsonRpcProvider;
71+
private signer: ethers.Wallet;
72+
73+
constructor(rpcUrl: string, privateKey: string) {
74+
this.provider = new ethers.JsonRpcProvider(rpcUrl);
75+
this.signer = new ethers.Wallet(privateKey, this.provider);
76+
}
77+
78+
/**
79+
* Record a Base chain transaction with ERC-8021 builder codes
80+
* attributing the original paper authors whose work informed the content.
81+
*/
82+
async recordAttribution(
83+
topicPath: string,
84+
papers: Paper[],
85+
): Promise<string | null> {
86+
try {
87+
// Build codes: agent + paper authors
88+
const codes = [COGITO_BUILDER_CODE];
89+
for (const paper of papers.slice(0, 3)) {
90+
const authorCodes = buildAuthorCodes(paper);
91+
for (const ac of authorCodes) {
92+
codes.push(`${ac.type}_${ac.identifier}`);
93+
}
94+
}
95+
96+
// Encode topic as data payload
97+
const topicHex = Buffer.from(`cogito:enrich:${topicPath}`).toString('hex');
98+
const suffix = encodeBuilderCodes(codes);
99+
const data = `0x${topicHex}${suffix}`;
100+
101+
// Self-transfer with builder code attribution
102+
const tx = await this.signer.sendTransaction({
103+
to: this.signer.address,
104+
value: 0,
105+
data,
106+
});
107+
108+
const authorCount = codes.length - 1;
109+
console.log(`[Base] Tx recorded: ${tx.hash} (topic: ${topicPath}, codes: ${COGITO_BUILDER_CODE}+${authorCount} authors)`);
110+
return tx.hash;
111+
} catch (err: any) {
112+
console.error(`[Base] Failed to record attribution: ${err.message}`);
113+
return null;
114+
}
115+
}
116+
117+
getAddress(): string {
118+
return this.signer.address;
119+
}
120+
}

0 commit comments

Comments
 (0)