Skip to content

Commit 6e94445

Browse files
authored
Merge pull request #130 from ModusCreateOrg/ADE-208
[ADE-208] fixes for ADE-66
2 parents da28da0 + ad1094d commit 6e94445

File tree

15 files changed

+35
-459
lines changed

15 files changed

+35
-459
lines changed

backend/PERPLEXITY_INTEGRATION.md

Lines changed: 10 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ This document describes the integration of the Perplexity API in the Medical Rep
55
## Overview
66

77
The integration enables the backend to leverage Perplexity's AI capabilities to:
8+
89
1. Explain medical text in simpler terms
910
2. Support custom chat completions for more flexible AI interactions
1011

@@ -47,25 +48,12 @@ The API key is securely managed using AWS Secrets Manager:
4748
2. The application retrieves the key at runtime using the AWS SDK
4849
3. The key is cached for 15 minutes to minimize API calls to Secrets Manager
4950

50-
### Service Functionality
51-
52-
The `PerplexityService` provides the following methods:
53-
54-
1. `createChatCompletion`: Sends a chat completion request to the Perplexity API
55-
2. `explainMedicalText`: Specializes in explaining medical text in simple terms
56-
57-
### API Endpoints
58-
59-
The `PerplexityController` exposes the following endpoints:
60-
61-
1. `POST /api/perplexity/explain`: Explains medical text in simpler terms
62-
2. `POST /api/perplexity/chat/completions`: Creates a custom chat completion
63-
6451
## Setup Instructions
6552

6653
### AWS Secrets Manager Setup
6754

6855
1. Create a secret in AWS Secrets Manager:
56+
6957
```
7058
aws secretsmanager create-secret \
7159
--name med-ai-perplexity-key \
@@ -80,9 +68,7 @@ The `PerplexityController` exposes the following endpoints:
8068
"Statement": [
8169
{
8270
"Effect": "Allow",
83-
"Action": [
84-
"secretsmanager:GetSecretValue"
85-
],
71+
"Action": ["secretsmanager:GetSecretValue"],
8672
"Resource": "arn:aws:secretsmanager:region:account-id:secret:med-ai-perplexity-key-*"
8773
}
8874
]
@@ -93,12 +79,12 @@ The `PerplexityController` exposes the following endpoints:
9379

9480
Configure the following environment variables:
9581

96-
| Variable | Description | Default Value |
97-
|----------|-------------|---------------|
82+
| Variable | Description | Default Value |
83+
| -------------------------------- | ----------------------------------------- | ----------------------- |
9884
| `PERPLEXITY_API_KEY_SECRET_NAME` | Name of the secret in AWS Secrets Manager | `med-ai-perplexity-key` |
99-
| `PERPLEXITY_MODEL` | Perplexity model to use | `mixtral-8x7b-instruct` |
100-
| `PERPLEXITY_MAX_TOKENS` | Maximum tokens to generate | `2048` |
101-
| `AWS_REGION` | AWS region for Secrets Manager | `us-east-1` |
85+
| `PERPLEXITY_MODEL` | Perplexity model to use | `mixtral-8x7b-instruct` |
86+
| `PERPLEXITY_MAX_TOKENS` | Maximum tokens to generate | `2048` |
87+
| `AWS_REGION` | AWS region for Secrets Manager | `us-east-1` |
10288

10389
## Local Development
10490

@@ -114,27 +100,15 @@ Then modify the `getApiKey` method in `PerplexityService` to check for this envi
114100

115101
The frontend can interact with the Perplexity API through the following endpoints:
116102

117-
### Explain Medical Text
118-
119-
```typescript
120-
// Example frontend code
121-
const explainMedicalText = async (text: string) => {
122-
const response = await axios.post('/api/perplexity/explain', {
123-
medicalText: text
124-
});
125-
return response.data.explanation;
126-
};
127-
```
128-
129103
### Custom Chat Completion
130104

131105
```typescript
132106
// Example frontend code
133107
const createChatCompletion = async (messages: any[]) => {
134108
const response = await axios.post('/api/perplexity/chat/completions', {
135109
messages: messages,
136-
temperature: 0.7
110+
temperature: 0.7,
137111
});
138112
return response.data.explanation;
139113
};
140-
```
114+
```

backend/src/app.module.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ describe('AppModule', () => {
5050
generateResponse: vi.fn().mockResolvedValue('test response'),
5151
analyzeMedicalDocument: vi.fn().mockResolvedValue({
5252
labValues: [],
53-
diagnoses: [],
5453
metadata: {
5554
isMedicalReport: true,
5655
confidence: 0.9,

backend/src/controllers/perplexity/perplexity.controller.ts

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Body, Controller, Post, HttpException, HttpStatus, Logger } from '@nestjs/common';
22
import { PerplexityService, PerplexityMessage } from '../../services/perplexity.service';
3-
import { ExplainMedicalTextDto, ChatCompletionDto, PerplexityResponseDto } from './perplexity.dto';
3+
import { ChatCompletionDto, PerplexityResponseDto } from './perplexity.dto';
44

55
/**
66
* Controller for Perplexity API endpoints
@@ -11,27 +11,6 @@ export class PerplexityController {
1111

1212
constructor(private readonly perplexityService: PerplexityService) {}
1313

14-
/**
15-
* Explains medical text in simpler terms
16-
*
17-
* @param dto The DTO containing medical text to explain
18-
* @returns The simplified explanation
19-
*/
20-
@Post('explain')
21-
async explainMedicalText(@Body() dto: ExplainMedicalTextDto): Promise<PerplexityResponseDto> {
22-
try {
23-
const explanation = await this.perplexityService.explainMedicalText(dto.medicalText);
24-
return { explanation };
25-
} catch (error: unknown) {
26-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
27-
this.logger.error(`Failed to explain medical text: ${errorMessage}`);
28-
throw new HttpException(
29-
'Failed to process medical text explanation',
30-
HttpStatus.INTERNAL_SERVER_ERROR,
31-
);
32-
}
33-
}
34-
3514
/**
3615
* Creates a custom chat completion
3716
*

backend/src/document-processor/controllers/document-processor.controller.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,7 @@ export class DocumentProcessorController {
155155
report.missingInformation = result.analysis.metadata.missingInformation;
156156
}
157157

158-
// Create summary from simplified explanation or diagnoses
159-
report.summary = result.simplifiedExplanation!;
158+
report.medicalComments = result.analysis.medicalComments!;
160159

161160
report.updatedAt = new Date().toISOString();
162161

backend/src/document-processor/services/aws-bedrock.service.spec.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ vi.mock('@aws-sdk/client-bedrock-runtime', () => {
8080
suggestions: 'Continue regular health maintenance.',
8181
},
8282
],
83-
diagnoses: [],
8483
metadata: {
8584
isMedicalReport: true,
8685
confidence: 0.95,
@@ -142,7 +141,7 @@ describe('AwsBedrockService', () => {
142141
suggestions: 'Continue regular health maintenance.',
143142
},
144143
],
145-
diagnoses: [],
144+
medicalComments: 'Patient hemoglobin levels are within normal range.',
146145
metadata: {
147146
isMedicalReport: true,
148147
confidence: 0.95,
@@ -293,10 +292,9 @@ describe('AwsBedrockService', () => {
293292
const invalidResponses = [
294293
null,
295294
{},
296-
{ labValues: [], diagnoses: [] }, // Missing metadata
295+
{ labValues: [] }, // Missing metadata
297296
{
298297
labValues: [],
299-
diagnoses: [],
300298
metadata: { isMedicalReport: 'not a boolean', confidence: 0.5, missingInformation: [] },
301299
},
302300
];
@@ -325,7 +323,7 @@ describe('AwsBedrockService', () => {
325323
suggestions: 'No action needed',
326324
},
327325
],
328-
diagnoses: [],
326+
medicalComments: '',
329327
metadata: {
330328
isMedicalReport: true,
331329
confidence: 0.9,

backend/src/document-processor/services/aws-bedrock.service.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export interface MedicalDocumentAnalysis {
2424
conclusion: string;
2525
suggestions: string;
2626
}>;
27-
diagnoses: Array<{ condition: string; details: string; recommendations: string }>;
27+
medicalComments: string;
2828
metadata: {
2929
isMedicalReport: boolean;
3030
confidence: number;
@@ -50,7 +50,7 @@ export class AwsBedrockService {
5050
1. Title/subject from content
5151
2. Category: "heart" (cardiac focus), "brain" (neurological focus), or "general" (all else)
5252
3. Lab values with ranges and status (normal/high/low)
53-
4. Diagnoses, findings, and recommendations
53+
4. Medical comments, if there are any, if not, return empty string
5454
5. Medical document verification with confidence level
5555
5656
Reference trusted sources: Mayo Clinic, Cleveland Clinic, CDC, NIH, WHO, AMA, etc.
@@ -60,7 +60,7 @@ Return ONLY a JSON object with this structure:
6060
"title": string,
6161
"category": string,
6262
"labValues": [{"name": string, "value": string, "unit": string, "normalRange": string, "status": "normal" | "high" | "low", "isCritical": boolean, "conclusion": string, "suggestions": string}],
63-
"diagnoses": [{"condition": string, "details": string, "recommendations": string}],
63+
"medicalComments": string,
6464
"metadata": {
6565
"isMedicalReport": boolean,
6666
"confidence": number,
@@ -365,7 +365,6 @@ Document text:
365365
typeof response.title !== 'string' ||
366366
typeof response.category !== 'string' ||
367367
!Array.isArray(response.labValues) ||
368-
!Array.isArray(response.diagnoses) ||
369368
!response.metadata
370369
) {
371370
throw new BadRequestException('Invalid medical analysis response structure');

backend/src/document-processor/services/document-processor.service.spec.ts

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,25 +37,23 @@ describe('DocumentProcessorService', () => {
3737
title: 'Test Report',
3838
category: 'general',
3939
labValues: [],
40-
diagnoses: [],
40+
medicalComments: '',
4141
metadata: {
4242
isMedicalReport: true,
4343
confidence: 0.9,
4444
missingInformation: [],
4545
},
4646
};
4747

48-
const simplifiedExplanation = 'This is a simple explanation of the medical document.';
49-
5048
// Create a new test-specific instance with proper mocking
5149
const testTextractService = { extractText: vi.fn() };
5250
const testBedrockService = { analyzeMedicalDocument: vi.fn() };
53-
const testPerplexityService = { explainMedicalText: vi.fn() };
51+
const testPerplexityService = { reviewMedicalAnalysis: vi.fn() };
5452

5553
// Set up mocks
5654
testTextractService.extractText.mockResolvedValue(extractedTextResult);
5755
testBedrockService.analyzeMedicalDocument.mockResolvedValue(medicalAnalysis);
58-
testPerplexityService.explainMedicalText.mockResolvedValue(simplifiedExplanation);
56+
testPerplexityService.reviewMedicalAnalysis.mockResolvedValue(medicalAnalysis);
5957

6058
// Create a fresh service instance with our mocks
6159
const testService = new DocumentProcessorService(
@@ -73,13 +71,10 @@ describe('DocumentProcessorService', () => {
7371
extractedTextResult.rawText,
7472
userId,
7573
);
76-
expect(testPerplexityService.explainMedicalText).toHaveBeenCalledWith(
77-
extractedTextResult.rawText,
78-
);
74+
expect(testPerplexityService.reviewMedicalAnalysis).toHaveBeenCalled();
7975
expect(result).toEqual({
8076
extractedText: extractedTextResult,
8177
analysis: medicalAnalysis,
82-
simplifiedExplanation,
8378
processingMetadata: expect.objectContaining({
8479
fileSize: fileBuffer.length,
8580
}),
@@ -94,7 +89,7 @@ describe('DocumentProcessorService', () => {
9489
// Create test-specific service with proper mocking
9590
const testTextractService = { extractText: vi.fn() };
9691
const testBedrockService = { analyzeMedicalDocument: vi.fn() };
97-
const testPerplexityService = { explainMedicalText: vi.fn() };
92+
const testPerplexityService = { reviewMedicalAnalysis: vi.fn() };
9893

9994
// Make the mock reject with an error
10095
testTextractService.extractText.mockRejectedValue(new Error('Failed to extract text'));
@@ -125,7 +120,7 @@ describe('DocumentProcessorService', () => {
125120
// Create test-specific service with proper mocking
126121
const testTextractService = { extractText: vi.fn() };
127122
const testBedrockService = { analyzeMedicalDocument: vi.fn() };
128-
const testPerplexityService = { explainMedicalText: vi.fn() };
123+
const testPerplexityService = { reviewMedicalAnalysis: vi.fn() };
129124

130125
// Create a fresh service instance with our mocks
131126
const testService = new DocumentProcessorService(
@@ -148,14 +143,13 @@ describe('DocumentProcessorService', () => {
148143
title: 'Document 1 Report',
149144
category: 'general',
150145
labValues: [],
151-
diagnoses: [],
146+
medicalComments: '',
152147
metadata: {
153148
isMedicalReport: true,
154149
confidence: 0.9,
155150
missingInformation: [],
156151
},
157152
},
158-
simplifiedExplanation: 'Simple explanation for document 1',
159153
processingMetadata: {
160154
processingTimeMs: 100,
161155
fileSize: 4,
@@ -173,14 +167,13 @@ describe('DocumentProcessorService', () => {
173167
title: 'Document 2 Report',
174168
category: 'general',
175169
labValues: [],
176-
diagnoses: [],
170+
medicalComments: '',
177171
metadata: {
178172
isMedicalReport: true,
179173
confidence: 0.9,
180174
missingInformation: [],
181175
},
182176
},
183-
simplifiedExplanation: 'Simple explanation for document 2',
184177
processingMetadata: {
185178
processingTimeMs: 100,
186179
fileSize: 4,

backend/src/document-processor/services/document-processor.service.ts

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { PerplexityService } from '../../services/perplexity.service';
1010
export interface ProcessedDocumentResult {
1111
extractedText: ExtractedTextResult;
1212
analysis: MedicalDocumentAnalysis;
13-
simplifiedExplanation?: string;
1413
processingMetadata: {
1514
processingTimeMs: number;
1615
fileSize: number;
@@ -62,6 +61,7 @@ export class DocumentProcessorService {
6261

6362
// Step 3: Review and verify analysis using Perplexity
6463
this.logger.log('Reviewing medical analysis with Perplexity');
64+
6565
let analysis: MedicalDocumentAnalysis;
6666

6767
try {
@@ -79,39 +79,18 @@ export class DocumentProcessorService {
7979
analysis = initialAnalysis;
8080
}
8181

82-
// Step 4: Generate simplified explanation using Perplexity
83-
let simplifiedExplanation: string | undefined;
84-
85-
try {
86-
if (analysis.metadata.isMedicalReport && extractedText.rawText) {
87-
this.logger.log('Generating simplified explanation');
88-
simplifiedExplanation = await this.perplexityService.explainMedicalText(
89-
extractedText.rawText,
90-
);
91-
this.logger.log('Simplified explanation generated successfully');
92-
}
93-
} catch (explanationError) {
94-
this.logger.error('Error generating simplified explanation', {
95-
error: explanationError instanceof Error ? explanationError.message : 'Unknown error',
96-
});
97-
// We don't want to fail the entire process if explanation fails
98-
simplifiedExplanation = undefined;
99-
}
100-
10182
const processingTime = Date.now() - startTime;
10283

10384
this.logger.log(`Document processing completed in ${processingTime}ms`, {
10485
isMedicalReport: analysis.metadata.isMedicalReport,
10586
confidence: analysis.metadata.confidence,
10687
labValueCount: analysis.labValues.length,
107-
hasExplanation: !!simplifiedExplanation,
10888
});
10989

11090
// Return combined result
11191
return {
11292
extractedText,
11393
analysis,
114-
simplifiedExplanation,
11594
processingMetadata: {
11695
processingTimeMs: processingTime,
11796
fileSize: fileBuffer.length,
@@ -175,14 +154,13 @@ export class DocumentProcessorService {
175154
title: 'Failed Document',
176155
category: 'general',
177156
labValues: [],
178-
diagnoses: [],
157+
medicalComments: '',
179158
metadata: {
180159
isMedicalReport: false,
181160
confidence: 0,
182161
missingInformation: ['Document processing failed'],
183162
},
184163
},
185-
simplifiedExplanation: undefined,
186164
processingMetadata: {
187165
processingTimeMs: 0,
188166
fileSize: doc.buffer.length,

backend/src/reports/models/report.model.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ export class Report {
5050
suggestions: string;
5151
}>;
5252

53-
@ApiProperty({ description: 'Summary of the report' })
54-
summary: string;
53+
@ApiProperty({ description: 'Medical comments related to the report' })
54+
medicalComments: string;
5555

5656
@ApiProperty({ description: 'Confidence score of the analysis (0-100)' })
5757
confidence: number;

0 commit comments

Comments
 (0)