santanche commited on
Commit
30f499c
·
1 Parent(s): c0d38ea

refactor (model and interface): new NER models and respective interface

Browse files
Files changed (6) hide show
  1. CONFIGURATION_GUIDE.md +52 -10
  2. Dockerfile +6 -0
  3. NER_AGENTS_GUIDE.md +421 -0
  4. README.md +7 -3
  5. server.py +84 -23
  6. static/index.html +59 -7
CONFIGURATION_GUIDE.md CHANGED
@@ -13,12 +13,33 @@ The Pub/Sub Multi-Agent System now supports saving and loading complete configur
13
  ### How to Save
14
 
15
  1. Configure your data sources and agents
16
- 2. Click the **"Save Config"** button in the top-right header
17
- 3. A JSON file will download automatically with the name pattern:
 
18
  ```
19
  pubsub-config-YYYY-MM-DD.json
20
  ```
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  ### What Gets Saved
23
 
24
  The configuration file includes:
@@ -32,9 +53,14 @@ The configuration file includes:
32
  - Model selection
33
  - Subscribe/Publish topics
34
  - Show result checkbox state
 
 
 
 
35
 
36
  ### Example Configuration File
37
 
 
38
  ```json
39
  {
40
  "version": "1.0",
@@ -44,16 +70,12 @@ The configuration file includes:
44
  {
45
  "label": "Schema",
46
  "content": "Tables:\n- customers (id, name, email)\n- orders (id, customer_id, total)"
47
- },
48
- {
49
- "label": "Rules",
50
- "content": "Always use LEFT JOIN for optional relationships"
51
  }
52
  ],
53
  "agents": [
54
  {
55
  "title": "SQL Generator",
56
- "prompt": "Generate SQL for: {question}\nSchema: {schema}\nRules: {rules}",
57
  "model": "phi3",
58
  "subscribeTopic": "START",
59
  "publishTopic": "SQL_GENERATED",
@@ -63,6 +85,22 @@ The configuration file includes:
63
  }
64
  ```
65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  ## Load Configuration
67
 
68
  ### How to Load
@@ -80,8 +118,12 @@ The configuration file includes:
80
 
81
  - **Current config is replaced**: All existing data sources and agents are removed
82
  - **New IDs assigned**: Loaded items get new unique IDs
83
- - **Logs cleared**: Execution log is reset
84
- - **Results cleared**: Final result box is cleared
 
 
 
 
85
  - **Validation**: File is checked for proper format before loading
86
 
87
  ### Error Handling
@@ -398,7 +440,7 @@ merge_configs("pipeline-a.json", "pipeline-b.json", "merged-pipeline.json")
398
  ### Issue: Agents not working after load
399
 
400
  **Cause**: Model might not be available
401
- **Solution**: Check agent "model" field matches available models (phi3, MedAIBase/MedGemma1.5:4b)
402
 
403
  ### Issue: Topics not matching after load
404
 
 
13
  ### How to Save
14
 
15
  1. Configure your data sources and agents
16
+ 2. **(Optional)** Check "Save results" to include execution results
17
+ 3. Click the **"Save Config"** button in the top-right header
18
+ 4. A JSON file will download automatically with the name pattern:
19
  ```
20
  pubsub-config-YYYY-MM-DD.json
21
  ```
22
 
23
+ ### Save Results Checkbox
24
+
25
+ The **"☑ Save results"** checkbox allows you to include execution results in the saved configuration.
26
+
27
+ **When checked**, the config includes:
28
+ - All configuration data (agents, data sources)
29
+ - Final Result box content
30
+ - NER Result box content
31
+ - Execution Log content
32
+
33
+ **When unchecked** (default):
34
+ - Only configuration data is saved
35
+ - No results or logs
36
+
37
+ **Use cases for saving results**:
38
+ - Document successful executions
39
+ - Share complete analysis with team
40
+ - Archive results with configuration
41
+ - Review past executions later
42
+
43
  ### What Gets Saved
44
 
45
  The configuration file includes:
 
53
  - Model selection
54
  - Subscribe/Publish topics
55
  - Show result checkbox state
56
+ - **Results** (if "Save results" checked):
57
+ - Final Result box content
58
+ - NER Result box content
59
+ - Execution Log content
60
 
61
  ### Example Configuration File
62
 
63
+ **Without Results**:
64
  ```json
65
  {
66
  "version": "1.0",
 
70
  {
71
  "label": "Schema",
72
  "content": "Tables:\n- customers (id, name, email)\n- orders (id, customer_id, total)"
 
 
 
 
73
  }
74
  ],
75
  "agents": [
76
  {
77
  "title": "SQL Generator",
78
+ "prompt": "Generate SQL for: {question}\nSchema: {schema}",
79
  "model": "phi3",
80
  "subscribeTopic": "START",
81
  "publishTopic": "SQL_GENERATED",
 
85
  }
86
  ```
87
 
88
+ **With Results** (when "Save results" is checked):
89
+ ```json
90
+ {
91
+ "version": "1.0",
92
+ "timestamp": "2026-02-01T10:30:00.000Z",
93
+ "userQuestion": "Extract medical entities from patient note",
94
+ "dataSources": [...],
95
+ "agents": [...],
96
+ "results": {
97
+ "finalResult": "--- Entity Extractor ---\n[{\"text\": \"diabetes\", \"entity_type\": \"PROBLEM\"}]",
98
+ "nerResult": "Patient has [diabetes:PROBLEM] and takes [metformin:TREATMENT]",
99
+ "executionLog": "[10:30:00] ℹ️ Starting...\n[10:30:05] ✅ Complete"
100
+ }
101
+ }
102
+ ```
103
+
104
  ## Load Configuration
105
 
106
  ### How to Load
 
118
 
119
  - **Current config is replaced**: All existing data sources and agents are removed
120
  - **New IDs assigned**: Loaded items get new unique IDs
121
+ - **Results restored** (if saved with results):
122
+ - Final Result box populated
123
+ - NER Result box populated
124
+ - Execution Log populated
125
+ - **Empty boxes** (if no results saved):
126
+ - All result boxes cleared
127
  - **Validation**: File is checked for proper format before loading
128
 
129
  ### Error Handling
 
440
  ### Issue: Agents not working after load
441
 
442
  **Cause**: Model might not be available
443
+ **Solution**: Check agent "model" field matches available models (phi3, cniongolo/biomistral)
444
 
445
  ### Issue: Topics not matching after load
446
 
Dockerfile CHANGED
@@ -51,6 +51,12 @@ ollama pull MedAIBase/MedGemma1.5:4b\n\
51
  echo "Pulling DeepSeek Coder model..."\n\
52
  ollama pull deepseek-coder:1.3b\n\
53
  \n\
 
 
 
 
 
 
54
  echo "Models ready! Starting FastAPI server..."\n\
55
  exec uvicorn server:app --host 0.0.0.0 --port 7860\n\
56
  ' > /app/start.sh && chmod +x /app/start.sh
 
51
  echo "Pulling DeepSeek Coder model..."\n\
52
  ollama pull deepseek-coder:1.3b\n\
53
  \n\
54
+ echo "Pulling Clinical NER model..."\n\
55
+ ollama pull samrawal/bert-base-uncased_clinical-ner\n\
56
+ \n\
57
+ echo "Pulling Anatomy NER model..."\n\
58
+ ollama pull OpenMed/OpenMed-NER-AnatomyDetect-BioPatient-108M\n\
59
+ \n\
60
  echo "Models ready! Starting FastAPI server..."\n\
61
  exec uvicorn server:app --host 0.0.0.0 --port 7860\n\
62
  ' > /app/start.sh && chmod +x /app/start.sh
NER_AGENTS_GUIDE.md ADDED
@@ -0,0 +1,421 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Named Entity Recognition (NER) Agents Guide
2
+
3
+ ## Overview
4
+
5
+ The Pub/Sub Multi-Agent System now includes specialized NER (Named Entity Recognition) agents that can extract medical entities from text. These agents work differently from regular LLM agents and have dedicated output displays.
6
+
7
+ ## Available NER Models
8
+
9
+ ### 1. Clinical NER Model
10
+ **Model**: `samrawal/bert-base-uncased_clinical-ner`
11
+
12
+ **Purpose**: Extract clinical entities from medical text
13
+
14
+ **Recognized Entity Types**:
15
+ - **PROBLEM**: Diseases, conditions, symptoms
16
+ - **TREATMENT**: Medications, procedures, therapies
17
+ - **TEST**: Diagnostic tests, lab results
18
+ - **OCCURRENCE**: Medical events, admissions
19
+
20
+ **Best for**:
21
+ - Clinical notes
22
+ - Patient reports
23
+ - Medical records
24
+ - Symptom descriptions
25
+
26
+ ### 2. Anatomy Detection Model
27
+ **Model**: `OpenMed/OpenMed-NER-AnatomyDetect-BioPatient-108M`
28
+
29
+ **Purpose**: Detect anatomical structures and patient information
30
+
31
+ **Recognized Entity Types**:
32
+ - **ANATOMY**: Body parts, organs, anatomical structures
33
+ - **PATIENT**: Patient demographics, identifiers
34
+ - **BIOMARKER**: Biological markers
35
+ - **CLINICAL_FINDING**: Clinical observations
36
+
37
+ **Best for**:
38
+ - Anatomical descriptions
39
+ - Radiology reports
40
+ - Surgical notes
41
+ - Physical examination records
42
+
43
+ ## How NER Agents Work
44
+
45
+ ### Different from Regular Agents
46
+
47
+ **Regular LLM Agents**:
48
+ - Process prompts with placeholders
49
+ - Generate text responses
50
+ - Use `{input}`, `{question}`, `{DataSource}` placeholders
51
+
52
+ **NER Agents**:
53
+ - Receive text through the message bus
54
+ - Extract named entities automatically
55
+ - Output JSON with entity information
56
+ - Display formatted results in NER Result box
57
+
58
+ ### Special Behavior
59
+
60
+ 1. **Automatic Processing**: No prompt template needed - just feed text via bus
61
+ 2. **Dual Output**:
62
+ - JSON result (for chaining to other agents)
63
+ - Formatted display (for human reading)
64
+ 3. **Dedicated Display**: NER Result box shows entities inline with text
65
+
66
+ ## Using NER Agents
67
+
68
+ ### Basic Setup
69
+
70
+ **Agent Configuration**:
71
+ ```
72
+ Title: Clinical Entity Extractor
73
+ Model: samrawal/bert-base-uncased_clinical-ner
74
+ Subscribe Topic: TEXT_TO_ANALYZE
75
+ Publish Topic: ENTITIES_FOUND
76
+ ☑ Show result in Final Result box
77
+ ```
78
+
79
+ **What happens**:
80
+ 1. Agent receives text from `TEXT_TO_ANALYZE` topic
81
+ 2. Extracts entities automatically
82
+ 3. Publishes JSON to `ENTITIES_FOUND` topic
83
+ 4. Shows JSON in Final Result box
84
+ 5. Shows formatted text in NER Result box
85
+
86
+ ### Output Format
87
+
88
+ **JSON Output** (in Final Result box):
89
+ ```json
90
+ [
91
+ {
92
+ "text": "diabetes",
93
+ "entity_type": "PROBLEM",
94
+ "start": 45,
95
+ "end": 53
96
+ },
97
+ {
98
+ "text": "metformin",
99
+ "entity_type": "TREATMENT",
100
+ "start": 78,
101
+ "end": 87
102
+ }
103
+ ]
104
+ ```
105
+
106
+ **Formatted Output** (in NER Result box):
107
+ ```
108
+ Patient reports history of [diabetes:PROBLEM] and is taking [metformin:TREATMENT].
109
+ ```
110
+
111
+ ## Example Workflows
112
+
113
+ ### Example 1: Clinical Note Analysis
114
+
115
+ **Data Source**:
116
+ - Label: `ClinicalNote`
117
+ - Content:
118
+ ```
119
+ Patient presents with chest pain and shortness of breath.
120
+ History of hypertension and diabetes mellitus type 2.
121
+ Currently taking lisinopril 10mg daily and metformin 500mg twice daily.
122
+ ECG shows ST elevation. Troponin levels elevated at 0.5 ng/mL.
123
+ ```
124
+
125
+ **Agents**:
126
+
127
+ **Agent 1: Clinical NER**
128
+ - Title: `Extract Clinical Entities`
129
+ - Model: `samrawal/bert-base-uncased_clinical-ner`
130
+ - Subscribe: `START`
131
+ - Publish: `CLINICAL_ENTITIES`
132
+ - Prompt: `{ClinicalNote}` *(text to analyze)*
133
+ - ☑ Show result
134
+
135
+ **Agent 2: Entity Summarizer**
136
+ - Title: `Summarize Findings`
137
+ - Model: `phi3`
138
+ - Subscribe: `CLINICAL_ENTITIES`
139
+ - Publish: *(empty)*
140
+ - Prompt:
141
+ ```
142
+ Based on these extracted entities:
143
+ {input}
144
+
145
+ Summarize the key clinical findings:
146
+ 1. Problems identified
147
+ 2. Treatments mentioned
148
+ 3. Tests performed
149
+ ```
150
+ - ☑ Show result
151
+
152
+ **Expected Results**:
153
+
154
+ *NER Result box*:
155
+ ```
156
+ Patient presents with [chest pain:PROBLEM] and [shortness of breath:PROBLEM].
157
+ History of [hypertension:PROBLEM] and [diabetes mellitus type 2:PROBLEM].
158
+ Currently taking [lisinopril:TREATMENT] 10mg daily and [metformin:TREATMENT] 500mg twice daily.
159
+ [ECG:TEST] shows ST elevation. [Troponin:TEST] levels elevated at 0.5 ng/mL.
160
+ ```
161
+
162
+ *Final Result box*:
163
+ ```
164
+ --- Extract Clinical Entities ---
165
+ [{"text": "chest pain", "entity_type": "PROBLEM", ...}, ...]
166
+
167
+ --- Summarize Findings ---
168
+ Key Clinical Findings:
169
+ 1. Problems: chest pain, shortness of breath, hypertension, diabetes
170
+ 2. Treatments: lisinopril, metformin
171
+ 3. Tests: ECG, Troponin
172
+ ```
173
+
174
+ ### Example 2: Anatomy Detection in Radiology Report
175
+
176
+ **User Question**: "Analyze this radiology report"
177
+
178
+ **Data Source**:
179
+ - Label: `RadiologyReport`
180
+ - Content:
181
+ ```
182
+ CT scan of the chest reveals mass in right upper lobe measuring 3.2 cm.
183
+ No evidence of mediastinal lymphadenopathy.
184
+ Heart size is normal. Lungs are clear bilaterally.
185
+ Liver and spleen appear unremarkable.
186
+ ```
187
+
188
+ **Agent Configuration**:
189
+
190
+ **Agent 1: Anatomy Detector**
191
+ - Title: `Detect Anatomical Structures`
192
+ - Model: `OpenMed/OpenMed-NER-AnatomyDetect-BioPatient-108M`
193
+ - Subscribe: `START`
194
+ - Publish: `ANATOMY_FOUND`
195
+ - Prompt: `{RadiologyReport}`
196
+ - ☑ Show result
197
+
198
+ **Expected NER Result**:
199
+ ```
200
+ CT scan of the [chest:ANATOMY] reveals mass in [right upper lobe:ANATOMY] measuring 3.2 cm.
201
+ No evidence of [mediastinal:ANATOMY] lymphadenopathy.
202
+ [Heart:ANATOMY] size is normal. [Lungs:ANATOMY] are clear bilaterally.
203
+ [Liver:ANATOMY] and [spleen:ANATOMY] appear unremarkable.
204
+ ```
205
+
206
+ ### Example 3: Multi-Stage Medical Analysis
207
+
208
+ **Workflow**: Extract entities → Categorize → Generate report
209
+
210
+ **Agent 1: Entity Extraction**
211
+ - Model: `samrawal/bert-base-uncased_clinical-ner`
212
+ - Subscribe: `START`
213
+ - Publish: `ENTITIES`
214
+ - ☑ Show result
215
+
216
+ **Agent 2: Entity Categorization**
217
+ - Model: `phi3`
218
+ - Subscribe: `ENTITIES`
219
+ - Publish: `CATEGORIZED`
220
+ - Prompt:
221
+ ```
222
+ Categorize these medical entities by type:
223
+ {input}
224
+
225
+ Group by: Problems, Treatments, Tests
226
+ ```
227
+ - ☑ Show result
228
+
229
+ **Agent 3: Report Generator**
230
+ - Model: `MedAIBase/MedGemma1.5:4b`
231
+ - Subscribe: `CATEGORIZED`
232
+ - Publish: *(empty)*
233
+ - Prompt:
234
+ ```
235
+ Generate a structured clinical summary based on:
236
+ {input}
237
+
238
+ Include assessment and plan.
239
+ ```
240
+ - ☑ Show result
241
+
242
+ ## NER Result Display Features
243
+
244
+ ### Inline Entity Markup
245
+
246
+ Entities are displayed inline with brackets and labels:
247
+ ```
248
+ [entity text:ENTITY_TYPE]
249
+ ```
250
+
251
+ ### Color Coding (Future Enhancement)
252
+
253
+ Different entity types could be color-coded:
254
+ - Problems: Red
255
+ - Treatments: Blue
256
+ - Tests: Green
257
+ - Anatomy: Purple
258
+
259
+ ### Entity Statistics (Future Enhancement)
260
+
261
+ Could show count of each entity type found.
262
+
263
+ ## Best Practices
264
+
265
+ ### 1. Choosing the Right NER Model
266
+
267
+ **Use Clinical NER for**:
268
+ - General clinical text
269
+ - Patient complaints
270
+ - Medical history
271
+ - Treatment plans
272
+
273
+ **Use Anatomy NER for**:
274
+ - Radiology reports
275
+ - Surgical notes
276
+ - Physical examination
277
+ - Anatomical descriptions
278
+
279
+ ### 2. Combining NER with Other Agents
280
+
281
+ **Pattern**: Extract → Analyze → Report
282
+ ```
283
+ NER Agent → Regular LLM → Medical LLM
284
+ ```
285
+
286
+ **Example**:
287
+ 1. NER extracts entities
288
+ 2. phi3 categorizes and structures
289
+ 3. MedGemma generates clinical assessment
290
+
291
+ ### 3. Data Source Configuration
292
+
293
+ **Option A: Use Data Source**
294
+ ```
295
+ Data Source Label: PatientNote
296
+ Content: [clinical text]
297
+
298
+ NER Agent Prompt: {PatientNote}
299
+ ```
300
+
301
+ **Option B: Use Message Bus**
302
+ ```
303
+ Previous Agent publishes clinical text
304
+ NER Agent receives via subscription
305
+ ```
306
+
307
+ ### 4. Output Handling
308
+
309
+ **For Human Review**:
310
+ - ☑ Check "Show result"
311
+ - Review in NER Result box
312
+
313
+ **For Further Processing**:
314
+ - Set Publish Topic
315
+ - Chain to analysis agents
316
+ - Use JSON output
317
+
318
+ ## Limitations
319
+
320
+ ### Current Limitations
321
+
322
+ 1. **No Prompt Templates**: NER agents don't support custom prompts
323
+ 2. **Fixed Entity Types**: Each model has predefined entity types
324
+ 3. **English Only**: Models trained on English medical text
325
+ 4. **Context Window**: Limited input text size
326
+
327
+ ### Workarounds
328
+
329
+ **For Long Texts**:
330
+ - Split into chunks
331
+ - Process separately
332
+ - Combine results
333
+
334
+ **For Custom Entities**:
335
+ - Use regular LLM with custom prompt
336
+ - Post-process NER output with another agent
337
+
338
+ ## Troubleshooting
339
+
340
+ ### Issue: No entities detected
341
+
342
+ **Causes**:
343
+ - Text doesn't contain medical terms
344
+ - Wrong NER model for the content type
345
+ - Text too short or too long
346
+
347
+ **Solutions**:
348
+ - Verify text contains medical content
349
+ - Try different NER model
350
+ - Check text length
351
+
352
+ ### Issue: Entities in wrong category
353
+
354
+ **Cause**: Model misclassification
355
+
356
+ **Solution**: Use post-processing agent to reclassify
357
+
358
+ ### Issue: NER Result box empty
359
+
360
+ **Causes**:
361
+ - "Show result" not checked
362
+ - Agent failed to execute
363
+ - No entities found
364
+
365
+ **Solutions**:
366
+ - Check "Show result" checkbox
367
+ - Review Execution Log for errors
368
+ - Verify input text
369
+
370
+ ## Advanced Usage
371
+
372
+ ### Combining Multiple NER Models
373
+
374
+ Run both NER models on same text:
375
+
376
+ **Agent 1: Clinical NER**
377
+ ```
378
+ Subscribe: START
379
+ Publish: CLINICAL_ENTITIES
380
+ ```
381
+
382
+ **Agent 2: Anatomy NER**
383
+ ```
384
+ Subscribe: START
385
+ Publish: ANATOMY_ENTITIES
386
+ ```
387
+
388
+ **Agent 3: Merge Results**
389
+ ```
390
+ Subscribe: CLINICAL_ENTITIES, ANATOMY_ENTITIES
391
+ Combine both outputs
392
+ ```
393
+
394
+ ### Entity Validation
395
+
396
+ Add validation agent after NER:
397
+
398
+ **Agent 1: NER Extraction**
399
+ ```
400
+ Model: Clinical NER
401
+ Publish: RAW_ENTITIES
402
+ ```
403
+
404
+ **Agent 2: Entity Validator**
405
+ ```
406
+ Model: MedGemma
407
+ Subscribe: RAW_ENTITIES
408
+ Validate medical accuracy
409
+ Publish: VALIDATED_ENTITIES
410
+ ```
411
+
412
+ ## Future Enhancements
413
+
414
+ Planned features:
415
+ - [ ] Color-coded entity display
416
+ - [ ] Entity statistics dashboard
417
+ - [ ] Confidence scores
418
+ - [ ] Custom entity types
419
+ - [ ] Multi-language support
420
+ - [ ] Entity linking (to medical ontologies)
421
+ - [ ] Batch processing
README.md CHANGED
@@ -25,10 +25,12 @@ This system allows you to create and orchestrate multiple AI agents that communi
25
 
26
  - 🔄 **Dynamic Agent Creation**: Add/remove agents on the fly
27
  - 📡 **Pub/Sub Architecture**: Event-driven agent orchestration
28
- - 🤖 **Multiple Models**: Support for phi3, llama3.2, gemma2, mistral, and biomistral
29
  - 🎯 **Topic-Based Routing**: Agents communicate through named topics
30
  - 📋 **Real-Time Logging**: Watch the message flow through the bus
31
  - ⚙️ **Customizable Prompts**: Each agent has its own prompt template
 
 
32
 
33
  ## How It Works
34
 
@@ -92,13 +94,15 @@ Final result displayed to user
92
 
93
  ## Supported Models
94
 
95
- This deployment includes three specialized models:
96
 
97
  - **phi3**: General-purpose model (3.8B parameters) - Great for text analysis, SQL generation, summarization, reasoning, and general tasks
98
  - **MedAIBase/MedGemma1.5:4b**: Medical/healthcare model (4B parameters) - Specialized for clinical reasoning, medical documentation, and healthcare-related tasks
99
  - **deepseek-coder:1.3b**: Code generation model (1.3B parameters) - Optimized for programming, code analysis, debugging, and technical documentation
 
 
100
 
101
- These models provide specialized capabilities for medical, coding, and general-purpose tasks.
102
 
103
  ## Architecture
104
 
 
25
 
26
  - 🔄 **Dynamic Agent Creation**: Add/remove agents on the fly
27
  - 📡 **Pub/Sub Architecture**: Event-driven agent orchestration
28
+ - 🤖 **Multiple Models**: Support for general, medical, coding, and NER models
29
  - 🎯 **Topic-Based Routing**: Agents communicate through named topics
30
  - 📋 **Real-Time Logging**: Watch the message flow through the bus
31
  - ⚙️ **Customizable Prompts**: Each agent has its own prompt template
32
+ - 🏷️ **Named Entity Recognition**: Dedicated NER agents with formatted output display
33
+ - 💾 **Save/Load with Results**: Save configurations including execution results and logs
34
 
35
  ## How It Works
36
 
 
94
 
95
  ## Supported Models
96
 
97
+ This deployment includes five specialized models:
98
 
99
  - **phi3**: General-purpose model (3.8B parameters) - Great for text analysis, SQL generation, summarization, reasoning, and general tasks
100
  - **MedAIBase/MedGemma1.5:4b**: Medical/healthcare model (4B parameters) - Specialized for clinical reasoning, medical documentation, and healthcare-related tasks
101
  - **deepseek-coder:1.3b**: Code generation model (1.3B parameters) - Optimized for programming, code analysis, debugging, and technical documentation
102
+ - **samrawal/bert-base-uncased_clinical-ner**: Clinical NER model - Extracts medical entities (diseases, symptoms, medications) from clinical text
103
+ - **OpenMed/OpenMed-NER-AnatomyDetect-BioPatient-108M**: Anatomy NER model (108M parameters) - Detects anatomical entities and patient-related information
104
 
105
+ These models provide specialized capabilities for medical, coding, NER, and general-purpose tasks.
106
 
107
  ## Architecture
108
 
server.py CHANGED
@@ -10,6 +10,7 @@ import json
10
  import asyncio
11
  from pathlib import Path
12
  import os
 
13
 
14
  app = FastAPI(title="Pub/Sub Multi-Agent System")
15
 
@@ -91,34 +92,89 @@ def create_event(event_type: str, **kwargs):
91
  def get_llm(model_name: str):
92
  return Ollama(model=model_name, temperature=0.1)
93
 
94
- # Execute agent
95
- async def execute_agent(agent: Agent, input_content: str, data_sources: List[DataSource], user_question: str) -> str:
96
- """Execute a single agent with the given input"""
97
- llm = get_llm(agent.model)
 
 
 
 
 
 
 
 
 
 
98
 
99
- # Start with the base prompt
100
- prompt_text = agent.prompt
101
 
102
- # Case-insensitive replacement helper
103
- def replace_case_insensitive(text: str, placeholder: str, value: str) -> str:
104
- """Replace placeholder in text, case insensitive"""
105
- import re
106
- pattern = re.compile(re.escape(placeholder), re.IGNORECASE)
107
- return pattern.sub(value, text)
108
 
109
- # Replace standard placeholders (case insensitive)
110
- prompt_text = replace_case_insensitive(prompt_text, "{input}", input_content)
111
- prompt_text = replace_case_insensitive(prompt_text, "{question}", user_question)
 
 
 
112
 
113
- # Replace data source placeholders (case insensitive)
114
- for ds in data_sources:
115
- placeholder = "{" + ds.label + "}"
116
- prompt_text = replace_case_insensitive(prompt_text, placeholder, ds.content)
117
 
118
- # Invoke LLM
119
- result = llm.invoke(prompt_text)
 
 
 
 
120
 
121
- return result if isinstance(result, str) else str(result)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
  # Main execution pipeline
124
  async def execute_pipeline(request: ExecutionRequest) -> AsyncGenerator[str, None]:
@@ -172,12 +228,17 @@ async def execute_pipeline(request: ExecutionRequest) -> AsyncGenerator[str, Non
172
 
173
  # Execute agent
174
  try:
175
- result = await execute_agent(agent, message_content, request.data_sources, request.user_question)
176
  yield create_event("agent_output", content=result)
177
 
178
  # If agent wants to show result, send it to frontend
179
  if agent.show_result:
180
  yield create_event("show_result", agent=agent.title, content=result)
 
 
 
 
 
181
 
182
  # Publish result to agent's publish topic (if specified)
183
  if agent.publish_topic:
 
10
  import asyncio
11
  from pathlib import Path
12
  import os
13
+ import re
14
 
15
  app = FastAPI(title="Pub/Sub Multi-Agent System")
16
 
 
92
  def get_llm(model_name: str):
93
  return Ollama(model=model_name, temperature=0.1)
94
 
95
+ # Check if model is NER model
96
+ def is_ner_model(model_name: str) -> bool:
97
+ """Check if the model is an NER model"""
98
+ ner_models = [
99
+ "samrawal/bert-base-uncased_clinical-ner",
100
+ "OpenMed/OpenMed-NER-AnatomyDetect-BioPatient-108M"
101
+ ]
102
+ return model_name in ner_models
103
+
104
+ # Format NER output for display
105
+ def format_ner_result(text: str, entities: List[Dict]) -> str:
106
+ """Format NER entities for human-readable display"""
107
+ if not entities:
108
+ return text
109
 
110
+ # Sort entities by start position
111
+ sorted_entities = sorted(entities, key=lambda x: x['start'])
112
 
113
+ result = []
114
+ last_end = 0
 
 
 
 
115
 
116
+ for entity in sorted_entities:
117
+ # Add text before entity
118
+ result.append(text[last_end:entity['start']])
119
+ # Add entity with label
120
+ result.append(f"[{text[entity['start']:entity['end']]}:{entity['entity_type']}]")
121
+ last_end = entity['end']
122
 
123
+ # Add remaining text
124
+ result.append(text[last_end:])
 
 
125
 
126
+ return ''.join(result)
127
+
128
+ # Execute agent
129
+ async def execute_agent(agent: Agent, input_content: str, data_sources: List[DataSource], user_question: str) -> tuple[str, Optional[List[Dict]]]:
130
+ """Execute a single agent with the given input. Returns (result, entities) where entities is for NER models."""
131
+ llm = get_llm(agent.model)
132
 
133
+ # Check if this is an NER model
134
+ if is_ner_model(agent.model):
135
+ # For NER models, perform entity recognition
136
+ # The input should be the text to analyze
137
+ prompt_text = f"Extract named entities from the following text. Return results as JSON with format: [{{'text': '...', 'entity_type': '...', 'start': int, 'end': int}}]\n\nText: {input_content}"
138
+
139
+ result = llm.invoke(prompt_text)
140
+ result_str = result if isinstance(result, str) else str(result)
141
+
142
+ # Try to parse JSON result
143
+ try:
144
+ # Extract JSON from response (might have extra text)
145
+ json_match = re.search(r'\[.*\]', result_str, re.DOTALL)
146
+ if json_match:
147
+ entities = json.loads(json_match.group())
148
+ # Return both JSON and entities for NER formatting
149
+ return result_str, entities
150
+ else:
151
+ return result_str, None
152
+ except:
153
+ return result_str, None
154
+ else:
155
+ # Regular LLM processing
156
+ # Start with the base prompt
157
+ prompt_text = agent.prompt
158
+
159
+ # Case-insensitive replacement helper
160
+ def replace_case_insensitive(text: str, placeholder: str, value: str) -> str:
161
+ """Replace placeholder in text, case insensitive"""
162
+ pattern = re.compile(re.escape(placeholder), re.IGNORECASE)
163
+ return pattern.sub(value, text)
164
+
165
+ # Replace standard placeholders (case insensitive)
166
+ prompt_text = replace_case_insensitive(prompt_text, "{input}", input_content)
167
+ prompt_text = replace_case_insensitive(prompt_text, "{question}", user_question)
168
+
169
+ # Replace data source placeholders (case insensitive)
170
+ for ds in data_sources:
171
+ placeholder = "{" + ds.label + "}"
172
+ prompt_text = replace_case_insensitive(prompt_text, placeholder, ds.content)
173
+
174
+ # Invoke LLM
175
+ result = llm.invoke(prompt_text)
176
+
177
+ return (result if isinstance(result, str) else str(result)), None
178
 
179
  # Main execution pipeline
180
  async def execute_pipeline(request: ExecutionRequest) -> AsyncGenerator[str, None]:
 
228
 
229
  # Execute agent
230
  try:
231
+ result, entities = await execute_agent(agent, message_content, request.data_sources, request.user_question)
232
  yield create_event("agent_output", content=result)
233
 
234
  # If agent wants to show result, send it to frontend
235
  if agent.show_result:
236
  yield create_event("show_result", agent=agent.title, content=result)
237
+
238
+ # If this is an NER agent with entities, also send formatted NER result
239
+ if entities and is_ner_model(agent.model):
240
+ formatted_text = format_ner_result(message_content, entities)
241
+ yield create_event("ner_result", agent=agent.title, formatted_text=formatted_text)
242
 
243
  # Publish result to agent's publish topic (if specified)
244
  if agent.publish_topic:
static/index.html CHANGED
@@ -20,14 +20,18 @@
20
  const [dataSources, setDataSources] = useState([]);
21
  const [userQuestion, setUserQuestion] = useState('');
22
  const [finalResult, setFinalResult] = useState('');
 
23
  const [logs, setLogs] = useState('');
24
  const [isExecuting, setIsExecuting] = useState(false);
 
25
  const fileInputRef = useRef(null);
26
 
27
  const models = [
28
  "phi3",
29
  "MedAIBase/MedGemma1.5:4b",
30
- "deepseek-coder:1.3b"
 
 
31
  ];
32
 
33
  const addLog = (message, type = 'info') => {
@@ -116,6 +120,15 @@
116
  }))
117
  };
118
 
 
 
 
 
 
 
 
 
 
119
  const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' });
120
  const url = URL.createObjectURL(blob);
121
  const a = document.createElement('a');
@@ -126,7 +139,7 @@
126
  document.body.removeChild(a);
127
  URL.revokeObjectURL(url);
128
 
129
- addLog('Configuration saved successfully', 'success');
130
  };
131
 
132
  const loadConfiguration = (event) => {
@@ -166,9 +179,18 @@
166
  }));
167
  setAgents(loadedAgents);
168
 
169
- setLogs('');
170
- setFinalResult('');
171
- addLog(`Configuration loaded: ${loadedDataSources.length} data sources, ${loadedAgents.length} agents`, 'success');
 
 
 
 
 
 
 
 
 
172
 
173
  } catch (error) {
174
  addLog(`Failed to load configuration: ${error.message}`, 'error');
@@ -186,6 +208,7 @@
186
  setIsExecuting(true);
187
  setLogs('');
188
  setFinalResult('');
 
189
 
190
  addLog('Initializing Pub/Sub Agent System...', 'info');
191
  addLog(`Total agents configured: ${agents.length}`, 'info');
@@ -257,6 +280,11 @@
257
  const separator = prev ? '\n\n--- ' + data.agent + ' ---\n\n' : '--- ' + data.agent + ' ---\n\n';
258
  return prev + separator + data.content;
259
  });
 
 
 
 
 
260
  } else if (data.type === 'no_subscribers') {
261
  addLog(`No subscribers for topic "${data.topic}"`, 'error');
262
  } else if (data.type === 'execution_complete') {
@@ -292,7 +320,19 @@
292
  </div>
293
 
294
  {/* Save/Load Buttons */}
295
- <div className="flex gap-2">
 
 
 
 
 
 
 
 
 
 
 
 
296
  <button
297
  onClick={saveConfiguration}
298
  disabled={agents.length === 0 && dataSources.length === 0}
@@ -532,6 +572,18 @@
532
 
533
  {/* Right Column - Logs and Results */}
534
  <div className="space-y-4">
 
 
 
 
 
 
 
 
 
 
 
 
535
  {/* Final Result */}
536
  <div className="bg-white rounded-lg shadow p-4">
537
  <label className="block text-sm font-semibold text-gray-700 mb-2">
@@ -553,7 +605,7 @@
553
  <textarea
554
  value={logs}
555
  readOnly
556
- className="w-full h-[calc(100vh-500px)] p-3 border border-gray-300 rounded-lg font-mono text-xs bg-gray-50 focus:outline-none overflow-auto"
557
  placeholder="Execution logs will appear here when you run the pipeline..."
558
  />
559
  </div>
 
20
  const [dataSources, setDataSources] = useState([]);
21
  const [userQuestion, setUserQuestion] = useState('');
22
  const [finalResult, setFinalResult] = useState('');
23
+ const [nerResult, setNerResult] = useState('');
24
  const [logs, setLogs] = useState('');
25
  const [isExecuting, setIsExecuting] = useState(false);
26
+ const [saveResults, setSaveResults] = useState(false);
27
  const fileInputRef = useRef(null);
28
 
29
  const models = [
30
  "phi3",
31
  "MedAIBase/MedGemma1.5:4b",
32
+ "deepseek-coder:1.3b",
33
+ "samrawal/bert-base-uncased_clinical-ner",
34
+ "OpenMed/OpenMed-NER-AnatomyDetect-BioPatient-108M"
35
  ];
36
 
37
  const addLog = (message, type = 'info') => {
 
120
  }))
121
  };
122
 
123
+ // Add results if checkbox is checked
124
+ if (saveResults) {
125
+ config.results = {
126
+ finalResult: finalResult,
127
+ nerResult: nerResult,
128
+ executionLog: logs
129
+ };
130
+ }
131
+
132
  const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' });
133
  const url = URL.createObjectURL(blob);
134
  const a = document.createElement('a');
 
139
  document.body.removeChild(a);
140
  URL.revokeObjectURL(url);
141
 
142
+ addLog('Configuration saved successfully' + (saveResults ? ' (with results)' : ''), 'success');
143
  };
144
 
145
  const loadConfiguration = (event) => {
 
179
  }));
180
  setAgents(loadedAgents);
181
 
182
+ // Load results if they exist
183
+ if (config.results) {
184
+ setFinalResult(config.results.finalResult || '');
185
+ setNerResult(config.results.nerResult || '');
186
+ setLogs(config.results.executionLog || '');
187
+ addLog(`Configuration loaded: ${loadedDataSources.length} data sources, ${loadedAgents.length} agents (with saved results)`, 'success');
188
+ } else {
189
+ setLogs('');
190
+ setFinalResult('');
191
+ setNerResult('');
192
+ addLog(`Configuration loaded: ${loadedDataSources.length} data sources, ${loadedAgents.length} agents`, 'success');
193
+ }
194
 
195
  } catch (error) {
196
  addLog(`Failed to load configuration: ${error.message}`, 'error');
 
208
  setIsExecuting(true);
209
  setLogs('');
210
  setFinalResult('');
211
+ setNerResult('');
212
 
213
  addLog('Initializing Pub/Sub Agent System...', 'info');
214
  addLog(`Total agents configured: ${agents.length}`, 'info');
 
280
  const separator = prev ? '\n\n--- ' + data.agent + ' ---\n\n' : '--- ' + data.agent + ' ---\n\n';
281
  return prev + separator + data.content;
282
  });
283
+ } else if (data.type === 'ner_result') {
284
+ setNerResult(prev => {
285
+ const separator = prev ? '\n\n--- ' + data.agent + ' ---\n\n' : '--- ' + data.agent + ' ---\n\n';
286
+ return prev + separator + data.formatted_text;
287
+ });
288
  } else if (data.type === 'no_subscribers') {
289
  addLog(`No subscribers for topic "${data.topic}"`, 'error');
290
  } else if (data.type === 'execution_complete') {
 
320
  </div>
321
 
322
  {/* Save/Load Buttons */}
323
+ <div className="flex gap-2 items-center">
324
+ <div className="flex items-center gap-2 mr-2">
325
+ <input
326
+ type="checkbox"
327
+ id="saveResults"
328
+ checked={saveResults}
329
+ onChange={(e) => setSaveResults(e.target.checked)}
330
+ className="w-4 h-4 text-green-600 focus:ring-green-500 border-gray-300 rounded"
331
+ />
332
+ <label htmlFor="saveResults" className="text-sm font-medium text-gray-700">
333
+ Save results
334
+ </label>
335
+ </div>
336
  <button
337
  onClick={saveConfiguration}
338
  disabled={agents.length === 0 && dataSources.length === 0}
 
572
 
573
  {/* Right Column - Logs and Results */}
574
  <div className="space-y-4">
575
+ {/* NER Result */}
576
+ <div className="bg-white rounded-lg shadow p-4">
577
+ <label className="block text-sm font-semibold text-gray-700 mb-2">
578
+ 🏷️ NER Result
579
+ </label>
580
+ <div
581
+ className="w-full h-48 p-3 border border-gray-300 rounded-lg text-sm bg-yellow-50 overflow-auto whitespace-pre-wrap font-mono"
582
+ >
583
+ {nerResult || "Named Entity Recognition results will appear here..."}
584
+ </div>
585
+ </div>
586
+
587
  {/* Final Result */}
588
  <div className="bg-white rounded-lg shadow p-4">
589
  <label className="block text-sm font-semibold text-gray-700 mb-2">
 
605
  <textarea
606
  value={logs}
607
  readOnly
608
+ className="w-full h-[calc(100vh-800px)] p-3 border border-gray-300 rounded-lg font-mono text-xs bg-gray-50 focus:outline-none overflow-auto"
609
  placeholder="Execution logs will appear here when you run the pipeline..."
610
  />
611
  </div>