Skip to content

Commit 0fde782

Browse files
authored
Merge pull request #57 from NeurProjects/release/0.1.10
v0.1.10 Update
2 parents 3f28049 + 45e6f64 commit 0fde782

File tree

14 files changed

+972
-135
lines changed

14 files changed

+972
-135
lines changed

CHANGELOGS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelogs
22

3+
## v0.1.10
4+
5+
- Message token tracking (model usage) for backend analysis
6+
- Fixes to solana-agent-kit implementation for decimal handling
7+
38
## v0.1.9
49

510
- Use correct messages when trimming the message context for gpt4o

package.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "neur-app",
3-
"version": "0.1.9",
3+
"version": "0.1.10",
44
"private": true,
55
"scripts": {
66
"vercel-build": "pnpm npx prisma generate && next build",
@@ -60,7 +60,7 @@
6060
"react-tweet": "^3.2.1",
6161
"rehype-raw": "^7.0.0",
6262
"remark-gfm": "^4.0.0",
63-
"solana-agent-kit": "^1.2.0",
63+
"solana-agent-kit": "1.3.2",
6464
"sonner": "^1.7.1",
6565
"swr": "^2.2.5",
6666
"tailwind-merge": "^2.5.5",
@@ -87,5 +87,10 @@
8787
"tailwindcss": "^3.4.17",
8888
"typescript": "^5.7.2"
8989
},
90-
"packageManager": "pnpm@9.15.1+sha512.1acb565e6193efbebda772702950469150cf12bcc764262e7587e71d19dc98a423dff9536e57ea44c49bdf790ff694e83c27be5faa23d67e0c033b583be4bfcf"
90+
"packageManager": "pnpm@9.15.1+sha512.1acb565e6193efbebda772702950469150cf12bcc764262e7587e71d19dc98a423dff9536e57ea44c49bdf790ff694e83c27be5faa23d67e0c033b583be4bfcf",
91+
"pnpm": {
92+
"patchedDependencies": {
93+
"solana-agent-kit": "patches/solana-agent-kit.patch"
94+
}
95+
}
9196
}

patches/solana-agent-kit.patch

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
diff --git a/dist/tools/trade.js b/dist/tools/trade.js
2+
index 280aa7564613238857477f791f7f7522a8faca83..47ad1ffee7884cb6a2e19e00d410fb2677ad8e2c 100644
3+
--- a/dist/tools/trade.js
4+
+++ b/dist/tools/trade.js
5+
@@ -29,7 +29,7 @@ async function trade(agent, outputMint, inputAmount, inputMint = constants_1.TOK
6+
`&amount=${scaledAmount}` +
7+
`&slippageBps=${slippageBps}` +
8+
`&onlyDirectRoutes=true` +
9+
- `&maxAccounts=20` +
10+
+ `&maxAccounts=64` +
11+
`${agent.config.JUPITER_FEE_BPS ? `&platformFeeBps=${agent.config.JUPITER_FEE_BPS}` : ""}`)).json();
12+
// Get serialized transaction
13+
let feeAccount;

pnpm-lock.yaml

Lines changed: 816 additions & 106 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-- CreateTable
2+
CREATE TABLE "token_stats" (
3+
"id" TEXT NOT NULL,
4+
"userId" TEXT NOT NULL,
5+
"messageIds" TEXT[],
6+
"totalTokens" INTEGER NOT NULL,
7+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
8+
"updatedAt" TIMESTAMP(3) NOT NULL,
9+
10+
CONSTRAINT "token_stats_pkey" PRIMARY KEY ("id")
11+
);
12+
13+
-- CreateIndex
14+
CREATE UNIQUE INDEX "token_stats_userId_key" ON "token_stats"("userId");
15+
16+
-- AddForeignKey
17+
ALTER TABLE "token_stats" ADD CONSTRAINT "token_stats_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- DropIndex
2+
DROP INDEX "token_stats_userId_key";
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
Warnings:
3+
4+
- Added the required column `completionTokens` to the `token_stats` table without a default value. This is not possible if the table is not empty.
5+
- Added the required column `promptTokens` to the `token_stats` table without a default value. This is not possible if the table is not empty.
6+
7+
*/
8+
-- AlterTable
9+
ALTER TABLE "token_stats" ADD COLUMN "completionTokens" INTEGER NOT NULL,
10+
ADD COLUMN "promptTokens" INTEGER NOT NULL;

prisma/schema.prisma

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ model User {
1818
1919
wallets Wallet[]
2020
conversations Conversation[]
21+
tokenStats TokenStat[]
2122
2223
@@map("users")
2324
}
@@ -62,6 +63,21 @@ model Message {
6263
@@map("messages")
6364
}
6465

66+
model TokenStat {
67+
id String @id @default(cuid())
68+
userId String
69+
messageIds String[]
70+
promptTokens Int
71+
completionTokens Int
72+
totalTokens Int
73+
createdAt DateTime @default(now())
74+
updatedAt DateTime @updatedAt
75+
76+
user User @relation(fields: [userId], references: [id])
77+
78+
@@map("token_stats")
79+
}
80+
6581
enum Visibility {
6682
PRIVATE
6783
PUBLIC

src/ai/providers.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const claude35Sonnet = anthropic('claude-3-5-sonnet-20241022');
2121
const openai = createOpenAI({
2222
baseURL: process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1',
2323
apiKey: process.env.OPENAI_API_KEY,
24+
compatibility: 'strict',
2425
});
2526
const gpt4o = openai('gpt-4o');
2627

src/ai/solana/dexscreener.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,15 @@ const OrdersResult = ({ orders }: { orders: DexScreenerOrder[] }) => {
124124
};
125125

126126
const TokenProfile = ({ pair }: { pair: DexScreenerPair }) => {
127+
if (!pair) {
128+
return (
129+
<Card className="bg-muted/50 p-4">
130+
<p className="text-sm text-muted-foreground">
131+
Token not found on DexScreener.
132+
</p>
133+
</Card>
134+
);
135+
}
127136
return (
128137
<Card className="space-y-4 bg-muted/50 p-4">
129138
<div className="flex items-center gap-3">
@@ -372,8 +381,6 @@ export const dexscreenerTools = {
372381
},
373382
);
374383

375-
console.log(response);
376-
377384
if (!response.ok) {
378385
throw new Error(
379386
`Failed to fetch token profile: ${response.statusText}`,
@@ -382,7 +389,7 @@ export const dexscreenerTools = {
382389

383390
const data = (await response.json()) as DexScreenerPairResponse;
384391

385-
if (!data.pairs.length) {
392+
if (data.pairs === null || !data.pairs.length) {
386393
throw new Error('No pair data found');
387394
}
388395

@@ -395,9 +402,10 @@ export const dexscreenerTools = {
395402
data: sortedPairs[0],
396403
};
397404
} catch (error) {
398-
throw new Error(
399-
`Failed to get token profile: ${error instanceof Error ? error.message : 'Unknown error'}`,
400-
);
405+
return {
406+
suppressFollowUp: false,
407+
data: null,
408+
};
401409
}
402410
},
403411
render: (raw: unknown) => {

src/ai/solana/solana.tsx

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -369,26 +369,14 @@ const swap = {
369369
throw new Error('Failed to retrieve agent');
370370
}
371371

372-
// temporary fix for agent kit
373-
// if inputMint != So11111111111111111111111111111111111111112
374-
// decimals = 6
375-
// else
376-
// decimals = 9
377-
const decimals =
378-
inputMint === 'So11111111111111111111111111111111111111112'
379-
? 10 ** 9
380-
: 10 ** 6;
381-
382-
const fixedAmount = (amount * decimals) / LAMPORTS_PER_SOL;
383-
384372
console.log('[swapTokens] inputMint', inputMint);
385373
console.log('[swapTokens] outputMint', outputMint);
386-
console.log('[swapTokens] fixedAmount', fixedAmount);
374+
console.log('[swapTokens] amount', amount);
387375
console.log('[swapTokens] slippageBps', slippageBps);
388376

389377
const signature = await agent.trade(
390378
new PublicKey(outputMint),
391-
fixedAmount,
379+
amount,
392380
new PublicKey(inputMint),
393381
slippageBps,
394382
);

src/app/(user)/chat/[id]/chat-interface.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,13 +339,19 @@ function ChatMessage({
339339
console.warn('Failed to parse image URL:', e);
340340
}
341341

342+
const thumbnailPattern = /_thumb\.(png|jpg|jpeg|gif)$/i;
343+
const isThumbnail = thumbnailPattern.test(src);
344+
345+
const width = isThumbnail ? 40 : 500;
346+
const height = isThumbnail ? 40 : 300;
347+
342348
// Fallback to Image component with default dimensions
343349
return (
344350
<Image
345351
src={src}
346352
alt={alt || ''}
347-
width={500}
348-
height={300}
353+
width={width}
354+
height={height}
349355
className="inline-block align-middle"
350356
/>
351357
);

src/app/api/chat/route.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { verifyUser } from '@/server/actions/user';
2626
import {
2727
dbCreateConversation,
2828
dbCreateMessages,
29+
dbCreateTokenStat,
2930
dbDeleteConversation,
3031
dbGetConversation,
3132
} from '@/server/db/queries';
@@ -69,7 +70,7 @@ export async function POST(req: Request) {
6970
revalidatePath('/api/conversations');
7071
}
7172

72-
await dbCreateMessages({
73+
const newUserMessage = await dbCreateMessages({
7374
messages: [
7475
{
7576
conversationId,
@@ -144,14 +145,16 @@ export async function POST(req: Request) {
144145

145146
maxSteps: 15,
146147
messages: relevantMessages,
147-
async onFinish({ response }) {
148+
async onFinish({ response, usage }) {
148149
if (!userId) return;
149150

150151
try {
151152
const sanitizedResponses = sanitizeResponseMessages(
152153
response.messages,
153154
);
154-
await dbCreateMessages({
155+
156+
// Create messages and get their IDs back
157+
const messages = await dbCreateMessages({
155158
messages: sanitizedResponses.map((message) => {
156159
return {
157160
conversationId,
@@ -161,6 +164,28 @@ export async function POST(req: Request) {
161164
}),
162165
});
163166

167+
// Save the token stats
168+
if (
169+
messages &&
170+
newUserMessage &&
171+
!isNaN(usage.promptTokens) &&
172+
!isNaN(usage.completionTokens) &&
173+
!isNaN(usage.totalTokens)
174+
) {
175+
const messageIds = newUserMessage
176+
.concat(messages)
177+
.map((message) => message.id);
178+
const { promptTokens, completionTokens, totalTokens } = usage;
179+
180+
await dbCreateTokenStat({
181+
userId,
182+
messageIds,
183+
promptTokens,
184+
completionTokens,
185+
totalTokens,
186+
});
187+
}
188+
164189
revalidatePath('/api/conversations');
165190
} catch (error) {
166191
console.error('[chat/route] Failed to save messages', error);

src/server/db/queries.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export async function dbCreateMessages({
6969
messages: Omit<PrismaMessage, 'id' | 'createdAt'>[];
7070
}) {
7171
try {
72-
return await prisma.message.createMany({
72+
return await prisma.message.createManyAndReturn({
7373
data: messages as Prisma.MessageCreateManyInput[],
7474
});
7575
} catch (error) {
@@ -162,3 +162,34 @@ export async function dbGetConversations({ userId }: { userId: string }) {
162162
return [];
163163
}
164164
}
165+
166+
export async function dbCreateTokenStat({
167+
userId,
168+
messageIds,
169+
totalTokens,
170+
promptTokens,
171+
completionTokens,
172+
}: {
173+
userId: string;
174+
messageIds: string[];
175+
totalTokens: number;
176+
promptTokens: number;
177+
completionTokens: number;
178+
}) {
179+
try {
180+
return await prisma.tokenStat.create({
181+
data: {
182+
userId,
183+
messageIds,
184+
totalTokens,
185+
promptTokens,
186+
completionTokens,
187+
},
188+
});
189+
} catch (error) {
190+
console.error('[DB Error] Failed to create token stats:', {
191+
error,
192+
});
193+
return null;
194+
}
195+
}

0 commit comments

Comments
 (0)