This commit is contained in:
12
.dockerignore
Normal file
12
.dockerignore
Normal file
@@ -0,0 +1,12 @@
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
node_modules
|
||||
npm-debug.log
|
||||
.git
|
||||
.gitignore
|
||||
.next
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
*.md
|
||||
.husky
|
||||
13
.env.example
13
.env.example
@@ -1,11 +1,4 @@
|
||||
NEXT_PUBLIC_CONTACT_EMAIL=
|
||||
POSTGRES_DATABASE=
|
||||
POSTGRES_HOST=
|
||||
POSTGRES_PASSWORD=
|
||||
POSTGRES_PRISMA_URL=
|
||||
POSTGRES_URL=
|
||||
POSTGRES_URL_NON_POOLING=
|
||||
POSTGRES_URL_NO_SSL=
|
||||
POSTGRES_USER=
|
||||
PURCHASE_REFLECTION_THRESHOLD=
|
||||
NUMBER_OF_WEEKS=
|
||||
OVHCLOUD_API_KEY=
|
||||
PURCHASE_REFLECTION_THRESHOLD=50
|
||||
NUMBER_OF_WEEKS=4
|
||||
|
||||
46
.gitea/workflows/deploy.yml
Normal file
46
.gitea/workflows/deploy.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
lint-build-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci
|
||||
else
|
||||
npm install
|
||||
fi
|
||||
|
||||
- name: Run linting
|
||||
run: npm run lint
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Deploy to VPS
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
|
||||
chmod 600 ~/.ssh/id_ed25519
|
||||
ssh-keyscan -H 51.210.247.57 >> ~/.ssh/known_hosts
|
||||
ssh debian@51.210.247.57 << 'EOF'
|
||||
cd /home/debian/synthetic-consumer-data
|
||||
git pull origin main
|
||||
cd /home/debian/gitea
|
||||
docker-compose up -d --build synthetic-consumer-data
|
||||
EOF
|
||||
64
Dockerfile
Normal file
64
Dockerfile
Normal file
@@ -0,0 +1,64 @@
|
||||
FROM node:20-alpine AS base
|
||||
|
||||
# Install dependencies only when needed
|
||||
FROM base AS deps
|
||||
RUN apk add --no-cache libc6-compat
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies
|
||||
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
|
||||
RUN \
|
||||
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
|
||||
elif [ -f package-lock.json ]; then npm ci; \
|
||||
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
|
||||
else echo "Lockfile not found." && exit 1; \
|
||||
fi
|
||||
|
||||
# Rebuild the source code only when needed
|
||||
FROM base AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
|
||||
# Build arguments for environment variables needed at build time
|
||||
ARG NEXT_PUBLIC_CONTACT_EMAIL
|
||||
ENV NEXT_PUBLIC_CONTACT_EMAIL=$NEXT_PUBLIC_CONTACT_EMAIL
|
||||
|
||||
# Next.js telemetry
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
RUN \
|
||||
if [ -f yarn.lock ]; then yarn run build; \
|
||||
elif [ -f package-lock.json ]; then npm run build; \
|
||||
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
|
||||
else echo "Lockfile not found." && exit 1; \
|
||||
fi
|
||||
|
||||
# Production image, copy all the files and run next
|
||||
FROM base AS runner
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
COPY --from=builder /app/public ./public
|
||||
|
||||
# Set the correct permission for prerender cache
|
||||
RUN mkdir .next
|
||||
RUN chown nextjs:nodejs .next
|
||||
|
||||
# Automatically leverage output traces to reduce image size
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
|
||||
USER nextjs
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
ENV PORT=3000
|
||||
ENV HOSTNAME="0.0.0.0"
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { Metadata } from 'next';
|
||||
import './globals.css';
|
||||
import { Analytics } from '@vercel/analytics/next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Synthetic Consumers Data Generator',
|
||||
@@ -16,7 +15,6 @@ export default function RootLayout({
|
||||
return (
|
||||
<html lang='en'>
|
||||
<body>{children}</body>
|
||||
<Analytics />
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
7
next.config.ts
Normal file
7
next.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { NextConfig } from 'next';
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
output: 'standalone'
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
9426
package-lock.json
generated
Normal file
9426
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -16,10 +16,9 @@
|
||||
"vercel:env": "vercel env pull .env"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/analytics": "^1.5.0",
|
||||
"ai": "^5.0.68",
|
||||
"axios": "^1.12.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"openai": "^4.77.0",
|
||||
"clsx": "^2.1.1",
|
||||
"crypto": "^1.0.1",
|
||||
"dotenv": "^16.4.5",
|
||||
@@ -34,7 +33,6 @@
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^18.4.3",
|
||||
"@commitlint/config-conventional": "^18.4.3",
|
||||
"@types/json-schema": "^7.0.15",
|
||||
"@types/node": "^22.10.1",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import 'dotenv/config';
|
||||
import { generateText, tool, jsonSchema } from 'ai';
|
||||
import type { JSONSchema7 } from 'json-schema';
|
||||
import OpenAI from 'openai';
|
||||
|
||||
const ovhAI = new OpenAI({
|
||||
apiKey: process.env.OVHCLOUD_API_KEY,
|
||||
baseURL: 'https://oai.endpoints.kepler.ai.cloud.ovh.net/v1'
|
||||
});
|
||||
|
||||
export interface BaseTool {
|
||||
readonly name: string;
|
||||
@@ -24,43 +28,61 @@ export async function makeRequest<T extends BaseTool>(
|
||||
toolDef: T
|
||||
): Promise<Record<string, unknown>> {
|
||||
try {
|
||||
const { steps } = await generateText({
|
||||
model: 'anthropic/claude-sonnet-4.5',
|
||||
const completion = await ovhAI.chat.completions.create({
|
||||
model: 'Meta-Llama-3_3-70B-Instruct',
|
||||
temperature: 1,
|
||||
tools: {
|
||||
[toolDef.name]: tool({
|
||||
max_tokens: 16000,
|
||||
tools: [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: toolDef.name,
|
||||
description: toolDef.input_schema.description || '',
|
||||
inputSchema: jsonSchema(toolDef.input_schema as JSONSchema7),
|
||||
execute: async args => args
|
||||
})
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: toolDef.input_schema.properties,
|
||||
required: toolDef.input_schema.required
|
||||
? [...toolDef.input_schema.required]
|
||||
: undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
tool_choice: {
|
||||
type: 'function',
|
||||
function: { name: toolDef.name }
|
||||
},
|
||||
toolChoice: {
|
||||
type: 'tool',
|
||||
toolName: toolDef.name
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content:
|
||||
'You are a data generation assistant. Generate realistic, diverse synthetic data. You must respond ONLY with the function call. Do not include any text outside the function call.'
|
||||
},
|
||||
prompt
|
||||
{
|
||||
role: 'user',
|
||||
content: prompt
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const toolCalls = steps.flatMap(step => step.toolCalls);
|
||||
const message = completion.choices[0]?.message;
|
||||
|
||||
if (!toolCalls || toolCalls.length === 0) {
|
||||
throw new Error('No tool calls found in response');
|
||||
if (!message?.tool_calls || message.tool_calls.length === 0) {
|
||||
throw new Error('No function call found in response');
|
||||
}
|
||||
|
||||
const typedCall = toolCalls[0] as unknown as {
|
||||
toolName: string;
|
||||
input: Record<string, unknown>;
|
||||
};
|
||||
const toolCall = message.tool_calls[0];
|
||||
|
||||
if (typedCall.toolName !== toolDef.name) {
|
||||
if (toolCall.function.name !== toolDef.name) {
|
||||
throw new Error(
|
||||
`Expected tool ${toolDef.name} but got ${typedCall.toolName}`
|
||||
`Expected tool ${toolDef.name} but got ${toolCall.function.name}`
|
||||
);
|
||||
}
|
||||
|
||||
return typedCall.input;
|
||||
const result = JSON.parse(toolCall.function.arguments);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('Error making request:', error);
|
||||
throw Error('Vercel AI Gateway client error.');
|
||||
throw Error('OVH AI Endpoints client error.');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user