Browse Source

chore: add agent dependencies and CI workflow

Add AI SDK, cache-manager, remend, zod for agent backend.
Add evalite, autoevals, vitest for eval suite.
Add golden-evals CI workflow, .npmrc, .dockerignore updates.
pull/6458/head
Ryan Waits 1 month ago
parent
commit
4b361e62dc
  1. 10
      .dockerignore
  2. 14
      .env.example
  3. 61
      .github/workflows/golden-evals.yml
  4. 1
      .npmrc
  5. 3
      Dockerfile
  6. 3137
      package-lock.json
  7. 15
      package.json
  8. 6
      scripts/fix-date-fns-types.sh

10
.dockerignore

@ -0,0 +1,10 @@
node_modules
dist
.env
.env.*
!.env.example
.git
evals
evalite.config.ts
*.md
!CHANGELOG.md

14
.env.example

@ -14,3 +14,17 @@ POSTGRES_PASSWORD=<INSERT_POSTGRES_PASSWORD>
ACCESS_TOKEN_SALT=<INSERT_RANDOM_STRING> ACCESS_TOKEN_SALT=<INSERT_RANDOM_STRING>
DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?connect_timeout=300&sslmode=prefer DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?connect_timeout=300&sslmode=prefer
JWT_SECRET_KEY=<INSERT_RANDOM_STRING> JWT_SECRET_KEY=<INSERT_RANDOM_STRING>
# AI / AGENT
ANTHROPIC_API_KEY=<INSERT_ANTHROPIC_API_KEY>
# DATA PROVIDERS
API_KEY_FINANCIAL_MODELING_PREP=<INSERT_FMP_API_KEY>
API_KEY_COINGECKO_DEMO=<INSERT_COINGECKO_DEMO_KEY>
# API_KEY_COINGECKO_PRO=<INSERT_COINGECKO_PRO_KEY>
DATA_SOURCES=["FINANCIAL_MODELING_PREP","COINGECKO","MANUAL"]
DATA_SOURCE_EXCHANGE_RATES=FINANCIAL_MODELING_PREP
DATA_SOURCE_IMPORT=FINANCIAL_MODELING_PREP
# EVALS (set after running scripts/seed-test-portfolio.sh)
# TEST_USER_ACCESS_TOKEN=<INSERT_ACCESS_TOKEN>

61
.github/workflows/golden-evals.yml

@ -0,0 +1,61 @@
name: Golden Evals
on:
# Run after deploy — trigger via Render deploy hook or manually
workflow_dispatch:
inputs:
api_base:
description: 'API base URL (e.g. https://ghostfolio-xxxx.onrender.com)'
required: false
# Also run on push to main (evals hit the deployed instance)
push:
branches: [main]
paths:
- 'apps/api/src/app/endpoints/agent/**'
- 'evals/**'
permissions:
contents: read
env:
NODE_VERSION: 22
jobs:
golden-evals:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Wait for service health
env:
API_BASE: ${{ inputs.api_base || secrets.RENDER_URL }}
run: |
echo "Waiting for ${API_BASE}/api/v1/health..."
for i in $(seq 1 30); do
if curl -sf "${API_BASE}/api/v1/health" > /dev/null 2>&1; then
echo "Service healthy!"
exit 0
fi
echo "Attempt $i/30 — retrying in 10s..."
sleep 10
done
echo "Service not healthy after 5 minutes"
exit 1
- name: Run golden evals
env:
API_BASE: ${{ inputs.api_base || secrets.RENDER_URL }}
TEST_USER_ACCESS_TOKEN: ${{ secrets.TEST_USER_ACCESS_TOKEN }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: npx evalite run --threshold 100 evals/golden/agent-golden.eval.ts

1
.npmrc

@ -0,0 +1 @@
legacy-peer-deps=true

3
Dockerfile

@ -18,7 +18,9 @@ COPY ./CHANGELOG.md CHANGELOG.md
COPY ./LICENSE LICENSE COPY ./LICENSE LICENSE
COPY ./package.json package.json COPY ./package.json package.json
COPY ./package-lock.json package-lock.json COPY ./package-lock.json package-lock.json
COPY ./.npmrc .npmrc
COPY ./prisma/schema.prisma prisma/ COPY ./prisma/schema.prisma prisma/
COPY ./scripts scripts/
RUN npm install RUN npm install
@ -38,6 +40,7 @@ WORKDIR /ghostfolio/dist/apps/api
# package.json was generated by the build process, however the original # package.json was generated by the build process, however the original
# package-lock.json needs to be used to ensure the same versions # package-lock.json needs to be used to ensure the same versions
COPY ./package-lock.json /ghostfolio/dist/apps/api/ COPY ./package-lock.json /ghostfolio/dist/apps/api/
COPY ./.npmrc /ghostfolio/dist/apps/api/
RUN npm install RUN npm install
COPY .config /ghostfolio/dist/apps/api/.config/ COPY .config /ghostfolio/dist/apps/api/.config/

3137
package-lock.json

File diff suppressed because it is too large

15
package.json

@ -34,7 +34,7 @@
"lint": "nx run-many --target=lint --all", "lint": "nx run-many --target=lint --all",
"ng": "nx", "ng": "nx",
"nx": "nx", "nx": "nx",
"postinstall": "prisma generate", "postinstall": "prisma generate && sh scripts/fix-date-fns-types.sh",
"prepare": "husky", "prepare": "husky",
"prisma": "prisma", "prisma": "prisma",
"replace-placeholders-in-build": "node ./replace.build.mjs", "replace-placeholders-in-build": "node ./replace.build.mjs",
@ -55,9 +55,10 @@
"workspace-generator": "nx workspace-generator" "workspace-generator": "nx workspace-generator"
}, },
"dependencies": { "dependencies": {
"@ai-sdk/anthropic": "^3.0.46",
"@angular/animations": "21.1.1", "@angular/animations": "21.1.1",
"@angular/cdk": "21.1.1", "@angular/cdk": "21.1.1",
"@angular/common": "21.1.1", "@angular/common": "^21.1.1",
"@angular/compiler": "21.1.1", "@angular/compiler": "21.1.1",
"@angular/core": "21.1.1", "@angular/core": "21.1.1",
"@angular/forms": "21.1.1", "@angular/forms": "21.1.1",
@ -83,14 +84,15 @@
"@nestjs/schedule": "6.1.1", "@nestjs/schedule": "6.1.1",
"@nestjs/serve-static": "5.0.4", "@nestjs/serve-static": "5.0.4",
"@openrouter/ai-sdk-provider": "0.7.2", "@openrouter/ai-sdk-provider": "0.7.2",
"@prisma/client": "6.19.0", "@prisma/client": "^6.19.0",
"@simplewebauthn/browser": "13.2.2", "@simplewebauthn/browser": "13.2.2",
"@simplewebauthn/server": "13.2.2", "@simplewebauthn/server": "13.2.2",
"ai": "4.3.16", "ai": "^6.0.97",
"alphavantage": "2.2.0", "alphavantage": "2.2.0",
"big.js": "7.0.1", "big.js": "7.0.1",
"bootstrap": "4.6.2", "bootstrap": "4.6.2",
"bull": "4.16.5", "bull": "4.16.5",
"cache-manager": "^7.2.8",
"chart.js": "4.5.1", "chart.js": "4.5.1",
"chartjs-adapter-date-fns": "3.0.0", "chartjs-adapter-date-fns": "3.0.0",
"chartjs-chart-treemap": "3.1.0", "chartjs-chart-treemap": "3.1.0",
@ -129,12 +131,14 @@
"passport-jwt": "4.0.1", "passport-jwt": "4.0.1",
"passport-openidconnect": "0.1.2", "passport-openidconnect": "0.1.2",
"reflect-metadata": "0.2.2", "reflect-metadata": "0.2.2",
"remend": "^1.2.1",
"rxjs": "7.8.1", "rxjs": "7.8.1",
"stripe": "20.3.0", "stripe": "20.3.0",
"svgmap": "2.14.0", "svgmap": "2.14.0",
"tablemark": "4.1.0", "tablemark": "4.1.0",
"twitter-api-v2": "1.29.0", "twitter-api-v2": "1.29.0",
"yahoo-finance2": "3.13.0", "yahoo-finance2": "3.13.0",
"zod": "^4.3.6",
"zone.js": "0.16.0" "zone.js": "0.16.0"
}, },
"devDependencies": { "devDependencies": {
@ -179,10 +183,12 @@
"@types/passport-openidconnect": "0.1.3", "@types/passport-openidconnect": "0.1.3",
"@typescript-eslint/eslint-plugin": "8.43.0", "@typescript-eslint/eslint-plugin": "8.43.0",
"@typescript-eslint/parser": "8.43.0", "@typescript-eslint/parser": "8.43.0",
"autoevals": "^0.0.132",
"eslint": "9.35.0", "eslint": "9.35.0",
"eslint-config-prettier": "10.1.8", "eslint-config-prettier": "10.1.8",
"eslint-plugin-import": "2.32.0", "eslint-plugin-import": "2.32.0",
"eslint-plugin-storybook": "10.1.10", "eslint-plugin-storybook": "10.1.10",
"evalite": "^0.19.0",
"husky": "9.1.7", "husky": "9.1.7",
"jest": "30.2.0", "jest": "30.2.0",
"jest-environment-jsdom": "30.2.0", "jest-environment-jsdom": "30.2.0",
@ -200,6 +206,7 @@
"ts-node": "10.9.2", "ts-node": "10.9.2",
"tslib": "2.8.1", "tslib": "2.8.1",
"typescript": "5.9.2", "typescript": "5.9.2",
"vitest": "^4.0.18",
"webpack-bundle-analyzer": "4.10.2" "webpack-bundle-analyzer": "4.10.2"
}, },
"engines": { "engines": {

6
scripts/fix-date-fns-types.sh

@ -0,0 +1,6 @@
#!/bin/sh
# date-fns v4 ships only .d.cts type files, which TypeScript's node10
# moduleResolution cannot resolve. Copy them to .d.ts so the type
# checker finds them when module is set to commonjs.
find node_modules/date-fns -name '*.d.cts' -exec sh -c 'for f; do cp "$f" "${f%.d.cts}.d.ts"; done' _ {} +
echo "date-fns: copied .d.cts -> .d.ts for node10 compatibility"
Loading…
Cancel
Save