Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
PORT=3000
EMBEDDINGS_API=http://localhost:8000
PATHWAY_API=https://api.pathway.md/v8/chat/completions
PATHWAY_MODEL=pathway-v8-0
SESSION_SECRET=session-secret
CORS_ORIGINS=https://frontend.com,https://another-frontend.com

Expand All @@ -15,4 +13,4 @@ AI_PROVIDER=Gemini

ANTHROPIC_API_KEY=<your_api_key>
GOOGLE_GENERATIVE_AI_API_KEY=<your_api_key>
PATHWAY_AI_API_KEY=<your_api_key>
SECUREGPT_API_KEY=<your_api_key>
323 changes: 178 additions & 145 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
},
"dependencies": {
"@ai-sdk/anthropic": "^1.2.4",
"@ai-sdk/azure": "^1.3.24",
"@ai-sdk/google": "^1.2.5",
"@ai-sdk/openai": "^1.3.6",
"@google/generative-ai": "^0.24.0",
Expand Down
20 changes: 0 additions & 20 deletions resources/pathway_prompt.md

This file was deleted.

42 changes: 42 additions & 0 deletions resources/specialist_prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# LLM Clinical Decision Support System Prompt

## Role and Goal:
You are a board-certified medical specialist. Your task is to provide a high-quality and actionable eConsult response to a Primary Care Provider (PCP). The response must be clear, concise, and based strictly on the information provided in the PCP's question and the attached clinical notes.

---

## Required Output Format and Content:
Please structure your response using the exact following three sections and formatting.

### 1. Assessment
* **Concise Summary:** Begin with a single sentence that encapsulates the patient's core clinical problem (e.g., *"This is a [age]-year-old with a history of [X, Y] presenting with [chief complaint] in the context of [relevant comorbidity or recent event]."*).
* **Key Findings:** Concisely synthesize the most clinically relevant findings from the provided history, exam, and data. Do not simply list facts; weave them into a coherent clinical picture.
* **Differential Diagnosis:** If applicable, provide a prioritized differential diagnosis, listing the most likely diagnosis first. Briefly state the primary reasons for your diagnostic considerations.
* **Overall Assessment:** End with an overall assessment of the patient's condition and whether this needs urgent attention.

### 2. Recommendations and Rationale
In a short paragraph form, provide specific, actionable recommendations. Group recommendations (e.g., Diagnostics, Therapeutics) if applicable. Therapeutic recommendations should include dosages and duration. At the end of this paragraph, please provide a concise rationale. Do not make harmful or incomplete recommendations. If there is insufficient information or the consult is of sufficient complexity as to require a human specialist review, please include in your recommendations appropriate referrals as indicated.

*Example Format:*
> Obtain a TSH and free T4. Start Escitalopram 10 mg daily.

### 3. Contingency Plan
This section outlines the "what if" scenarios.
* **Escalation Triggers:** List specific, objective "red flags" that should prompt the PCP to re-evaluate or escalate care (e.g., send to the Emergency Department, refer to specialist). Include specific thresholds (e.g., *"If systolic blood pressure drops below 90 mmHg,"* *"If PHQ-9 score increases by more than 5 points,"* *"Development of new neurological deficits"*).

### 4. Citations
Provide any citations or guidelines relevant to the recommendations. These should be brief.

---

## Tone and Style:
* **Tone:** Maintain a professional tone. Be direct, concise, and avoid excessive language such as *"thank you for this consult..."*
* **Clarity:** Use clear and unambiguous language. Avoid overly academic jargon. The goal is to be easily understood by a busy generalist.
* **Conciseness:** Be brief and to the point. The total number of words in the entire response (excluding citations) should be less than 150 words.

---

## Input Context:
The PCP's clinical question and recent clinical notes follow:
{{question}}
{{notes}}
10 changes: 5 additions & 5 deletions sample_requests.http
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
### Gemini followup questions
GET {{assist-pc-backend-url}}/followup-questions

### Pathway chatbot question
POST {{assist-pc-backend-url}}/ask-pathway
### Specialist chatbot question
POST {{assist-pc-backend-url}}/ask-specialist
Content-Type: application/json

{
"question": "What further procedures would you recommend?"
}

### Pathway chatbot question streamed
POST {{assist-pc-backend-url}}/ask-pathway-streamed
### Specialist chatbot question streamed
POST {{assist-pc-backend-url}}/ask-specialist-streamed
Content-Type: application/json

{
"question": "Understood. Any other diagnoses?"
"question": "Do any other diagnoses fit the description?"
}

### Clinical question with matched template streamed
Expand Down
4 changes: 2 additions & 2 deletions src/app.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import { AppService } from './app.service';
import { LLMResponse } from './models/llmResponse';
import { TemplateSelectorService } from './template-selector/template-selector.service';
import { HttpModule } from '@nestjs/axios';
import { PathwayService } from './pathway/pathway.service';
import { SpecialistAiService } from './specialist-ai/specialist-ai.service';

describe('AppController', () => {
let appController: AppController;

beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService, TemplateSelectorService, PathwayService],
providers: [AppService, TemplateSelectorService, SpecialistAiService],
imports: [HttpModule],
}).compile();

Expand Down
32 changes: 16 additions & 16 deletions src/app.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ export class AppController {

const response = await this.appService.postReferralQuestion(request);
session[SessionKeys.REFERRAL_RESPONSE] = response;
// reset Pathway conversation history on new referral request
session[SessionKeys.PREVIOUS_PATHWAY_CONVERSATIONS] = [];
// reset Specialist AI conversation history on new referral request
session[SessionKeys.PREVIOUS_SPECIALIST_CONVERSATIONS] = [];

return response;
}
Expand All @@ -64,7 +64,7 @@ export class AppController {
return await this.appService.postReferralQuestionStreamed(request, session);
}

@Post('/ask-pathway')
@Post('/ask-specialist')
@ApiBody({
schema: {
properties: {
Expand All @@ -74,22 +74,22 @@ export class AppController {
})
@ApiCreatedResponse({
description:
'Successfully received Pathway AI response to a clarifying question.',
'Successfully received Specialist AI response to a clarifying question.',
type: SpecialistAIResponse,
})
async postPathwayQuestion(
async postSpecialistQuestion(
@Session() session: Record<string, any>,
@Body('question') question: string,
): Promise<SpecialistAIResponse> {
this.logger.debug('controller request', question);
this.logger.debug('session', session);
const response: SpecialistAIResponse =
await this.appService.postPathwayQuestion(question, session);
if (session[SessionKeys.PREVIOUS_PATHWAY_CONVERSATIONS] == null) {
session[SessionKeys.PREVIOUS_PATHWAY_CONVERSATIONS] = [];
await this.appService.postSpecialistQuestion(question, session);
if (session[SessionKeys.PREVIOUS_SPECIALIST_CONVERSATIONS] == null) {
session[SessionKeys.PREVIOUS_SPECIALIST_CONVERSATIONS] = [];
}
(
session[SessionKeys.PREVIOUS_PATHWAY_CONVERSATIONS] as Record<
session[SessionKeys.PREVIOUS_SPECIALIST_CONVERSATIONS] as Record<
string,
SpecialistAIResponse
>[]
Expand All @@ -99,7 +99,7 @@ export class AppController {
return response;
}

@Post('/ask-pathway-streamed')
@Post('/ask-specialist-streamed')
@Sse()
@ApiBody({
schema: {
Expand All @@ -110,19 +110,19 @@ export class AppController {
})
@ApiOkResponse({
description:
'Successfully received streamed Pathway AI response to a clarifying question.',
'Successfully received streamed Specialist AI response to a clarifying question.',
type: SpecialistAIResponse,
})
postPathwayQuestionStreamed(
postSpecialistQuestionStreamed(
@Session() session: Record<string, any>,
@Body('question') question: string,
): Observable<{ data: SpecialistAIResponse }> {
this.logger.debug('controller request', question);
this.logger.debug('session', session);
if (session[SessionKeys.PREVIOUS_PATHWAY_CONVERSATIONS] == null) {
session[SessionKeys.PREVIOUS_PATHWAY_CONVERSATIONS] = [];
if (session[SessionKeys.PREVIOUS_SPECIALIST_CONVERSATIONS] == null) {
session[SessionKeys.PREVIOUS_SPECIALIST_CONVERSATIONS] = [];
}
return this.appService.postPathwayQuestionStreamed(question, session);
return this.appService.postSpecialistQuestionStreamed(question, session);
}

@Get('/followup-questions')
Expand All @@ -133,7 +133,7 @@ export class AppController {
})
generateFollowupQuestions(
@Session() session: Record<string, any>,
): Promise<string[]> {
): Promise<{ questions: string[] }> {
this.logger.debug('controller request for followup questions generation');
this.logger.debug('session', session);
return this.appService.generateFollowupQuestions(session);
Expand Down
10 changes: 8 additions & 2 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@ import { AppService } from './app.service';
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { TemplateSelectorModule } from './template-selector/template-selector.module';
import { PathwayModule } from './pathway/pathway.module';
import { SpecialistAiModule } from './specialist-ai/specialist-ai.module';
import { LlmSelectorModule } from './llm-selector/llm-selector.module';

@Module({
imports: [ConfigModule.forRoot(), TemplateSelectorModule, PathwayModule],
imports: [
ConfigModule.forRoot(),
TemplateSelectorModule,
SpecialistAiModule,
LlmSelectorModule,
],
controllers: [AppController],
providers: [AppService],
})
Expand Down
Loading