diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 0000000..7dc2da2 --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,25 @@ +name: Pull Request + +on: [pull_request] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + project: [ui, server] + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Install dependencies + run: npm install + working-directory: ${{ matrix.project }} + - name: Run lint + run: npm run lint + working-directory: ${{ matrix.project }} + - name: Build + run: npm run build + working-directory: ${{ matrix.project }} diff --git a/server/.eslintrc.json b/server/.eslintrc.json index 117cfc9..8a3dae1 100644 --- a/server/.eslintrc.json +++ b/server/.eslintrc.json @@ -11,6 +11,10 @@ "deprecation", "unicorn" ], + "ignorePatterns": [ + "node_modules/", + "dist/" + ], "rules": { "unicorn/filename-case": "error", "indent": [ diff --git a/server/package.json b/server/package.json index 1398c55..3e74091 100644 --- a/server/package.json +++ b/server/package.json @@ -1,7 +1,7 @@ { - "name": "langtrace-ingest", + "name": "langtrace-server", "version": "1.0.0", - "main": "src/ingestor_server.ts", + "main": "src/ingestor-server.ts", "scripts": { "start:langtrace": "ts-node src/langtrace-server.ts", "start:ingestor": "ts-node src/ingest-server.ts", diff --git a/server/src/repositories/api-repository.ts b/server/src/repositories/api-repository.ts index f2597b1..e8cfe6c 100644 --- a/server/src/repositories/api-repository.ts +++ b/server/src/repositories/api-repository.ts @@ -31,7 +31,7 @@ export class ApiRepository { private createMatchForFilters(feedbackFilters?: FeedbackFilters) { interface MongoDBQuery { - $or?: { [key: string]: any }[]; + $or?: { [key: string]: unknown }[]; } const feedbackFilter: MongoDBQuery = {}; diff --git a/server/src/repositories/ingest-repository.ts b/server/src/repositories/ingest-repository.ts index 36fea5c..3fa7489 100644 --- a/server/src/repositories/ingest-repository.ts +++ b/server/src/repositories/ingest-repository.ts @@ -48,6 +48,4 @@ export class IngestRepository { return await collection.updateOne({ 'feedback.id': feedbackId }, { $set: setOperation }); } - - } diff --git a/server/src/services/langchain-to-langtrace-service.ts b/server/src/services/langchain-to-langtrace-service.ts index f91c08d..fa53221 100644 --- a/server/src/services/langchain-to-langtrace-service.ts +++ b/server/src/services/langchain-to-langtrace-service.ts @@ -31,10 +31,10 @@ export class LangchainToLangtraceService { return langchainData.run_id; } - async updateTrace(trace_id: string, langchainData: TraceData): Promise { + async updateTrace(traceId: string, langchainData: TraceData): Promise { this.convertToDates(langchainData); - const updateResult = await this.repository.updateTrace(trace_id, langchainData); + const updateResult = await this.repository.updateTrace(traceId, langchainData); return updateResult.matchedCount > 0; } diff --git a/server/src/services/trace-service.test.ts b/server/src/services/trace-service.test.ts index 12ea39f..49a3666 100644 --- a/server/src/services/trace-service.test.ts +++ b/server/src/services/trace-service.test.ts @@ -10,6 +10,8 @@ describe('TraceService', () => { let service: TraceService; let mockRepository: jest.Mocked; + const mockProjectId = 'projectId'; + beforeEach(() => { mockRepository = new ApiRepository() as any; service = new TraceService(); @@ -26,7 +28,12 @@ describe('TraceService', () => { run_type: '', session_name: '', start_time: '', - 'run_id': mockTraceId, name: 'Test Trace' + 'run_id': mockTraceId, + name: 'Test Trace', + execution_order: 0, + trace_id: null, + dotted_order: null, + }; @@ -42,17 +49,26 @@ describe('TraceService', () => { mockRepository.getTraces.mockResolvedValue(expectedResult); mockRepository.getFeedbackCounts.mockResolvedValue( - [{ key: 'useful', counts: { 'true': 0, 'false': 0 } }]); + [{ + key: 'useful', + feedbackType: 'value', + counts: { 'true': 0, 'false': 0 } + }] + ); - mockRepository.getLatencyPercentile.mockResolvedValue( [ + mockRepository.getLatencyPercentile.mockResolvedValue([ { percentile: 0, latency: 0 } as TracePercentile, ] ); - const result = await service.getTopLevelTraces(); + const result = await service.getTopLevelTraces(mockProjectId); expect(result).toEqual({ - feedback_counts: [{ key: 'useful', counts: { 'true': 0, 'false': 0 } }], + feedback_counts: [{ + key: 'useful', + feedbackType: 'value', + counts: { 'true': 0, 'false': 0 } + }], latency_percentiles: [{ percentile: 0, latency: 0 }], traces: expectedResult } satisfies TracesResponse); @@ -66,30 +82,28 @@ describe('TraceService', () => { it('should return null if no trace is found', async () => { mockRepository.getTraceTreeById.mockResolvedValue([]); - const result = await service.getTraceTreeByRunId(mockTraceId); + const result = await service.getTraceTreeByRunId(mockProjectId, mockTraceId); expect(result).toBeNull(); - expect(mockRepository.getTraceTreeById).toHaveBeenCalledWith(mockTraceId); + expect(mockRepository.getTraceTreeById).toHaveBeenCalledWith(mockProjectId, mockTraceId); }); it('should throw an error if multiple traces are found', async () => { mockRepository.getTraceTreeById.mockResolvedValue([mockTraceDocument, mockTraceDocument]); - await expect(service.getTraceTreeByRunId(mockTraceId)).rejects.toThrow('Trace not found'); - expect(mockRepository.getTraceTreeById).toHaveBeenCalledWith(mockTraceId); + await expect(service.getTraceTreeByRunId(mockProjectId, mockTraceId)) + .rejects.toThrow('Trace not found'); + expect(mockRepository.getTraceTreeById).toHaveBeenCalledWith(mockProjectId, mockTraceId); }); it('should return a trace document if exactly one trace is found', async () => { mockRepository.getTraceTreeById.mockResolvedValue([mockTraceDocument]); - const result = await service.getTraceTreeByRunId(mockTraceId); + const result = await service.getTraceTreeByRunId(mockProjectId, mockTraceId); expect(result).toEqual(mockTraceDocument); - expect(mockRepository.getTraceTreeById).toHaveBeenCalledWith(mockTraceId); + expect(mockRepository.getTraceTreeById).toHaveBeenCalledWith(mockProjectId, mockTraceId); }); }); - - -}) -; +}); diff --git a/ui/.eslintrc.json b/ui/.eslintrc.json index b175a1f..c8f7d2b 100644 --- a/ui/.eslintrc.json +++ b/ui/.eslintrc.json @@ -3,6 +3,11 @@ "plugins": [ "unicorn" ], + "ignorePatterns": [ + "node_modules/", + ".next/", + "dist/" + ], "rules": { "unicorn/filename-case": [ "error", diff --git a/ui/src/pages/api/auth/[...nextauth].ts b/ui/src/pages/api/auth/[...nextauth].ts index 5ec8d65..c0c2c53 100644 --- a/ui/src/pages/api/auth/[...nextauth].ts +++ b/ui/src/pages/api/auth/[...nextauth].ts @@ -1,14 +1,19 @@ -import NextAuth, { NextAuthOptions } from 'next-auth'; +import NextAuth, { NextAuthOptions, Profile } from 'next-auth'; import GithubProvider from 'next-auth/providers/github'; import { NextApiRequest, NextApiResponse } from 'next'; const enableAuth = process.env.NEXTAUTH_ENABLE === 'true'; +interface GitHubProfile extends Profile { + login: string; + +} + const options = { providers: [ GithubProvider({ - clientId: process.env.NEXTAUTH_GITHUB_ID, - clientSecret: process.env.NEXTAUTH_GITHUB_SECRET, + clientId: process.env.NEXTAUTH_GITHUB_ID!, + clientSecret: process.env.NEXTAUTH_GITHUB_SECRET!, authorization: { params: { scope: 'read:user,user:email,read:org', @@ -20,10 +25,10 @@ const options = { async signIn({ account, profile }) { if (account?.provider === 'github' && (process.env.NEXTAUTH_GITHUB_ORGANISATION ?? '').trim() !== '') { - const orgName = process.env.NEXTAUTH_GITHUB_ORGANISATION; const token = account.access_token; - const url = `https://api.github.com/orgs/${orgName}/members/${profile?.login}`; + const url = + `https://api.github.com/orgs/${orgName}/members/${(profile as GitHubProfile).login}`; const response = await fetch(url, { headers: { Authorization: `token ${token}`, diff --git a/ui/src/pages/projects/index.tsx b/ui/src/pages/projects/index.tsx index 099cc7d..f2565f7 100644 --- a/ui/src/pages/projects/index.tsx +++ b/ui/src/pages/projects/index.tsx @@ -1,7 +1,5 @@ import React from 'react'; import { getProjects } from '@/services/projects-service'; -import Breadcrumb from '@/components/Breadcrumb'; -import Link from 'next/link'; import Panel from '@/components/Panel'; import OutlineButton from '@/components/OutlineButton'; import AppBar from '@/components/AppBar'; @@ -21,7 +19,11 @@ const Projects: React.FC = ({ projects }) => {

Projects

{projects.map((project, index) => ( -

{project}

+

+ + {project} + +

))}
;