Make a schematic flowchart for the diagnosis and management of bipolar disorder in accordance with the latest guidelines

Reading File
Searching the Web

bipolar disorder diagnosis management guidelines 2024 2025 APA CANMAT

Finding Sources
Reading Web Page

https://pmc.ncbi.nlm.nih.gov/articles/PMC12900052

Finding Sources
Reading Web Page

https://pmc.ncbi.nlm.nih.gov/articles/PMC12900052

Reading File
Searching PubMed

bipolar disorder management guidelines

Reading File
Reading File
Running Command

mkdir -p /home/daytona/workspace/bipolar-flowchart && ls /home/daytona/workspace/

Writing File

~/bipolar-flowchart/bipolar_flowchart.py

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, FancyArrowPatch
import numpy as np

fig, ax = plt.subplots(figsize=(26, 44))
ax.set_xlim(0, 26)
ax.set_ylim(0, 44)
ax.axis('off')
fig.patch.set_facecolor('#F7F9FC')

# ── colour palette ──────────────────────────────────────────────────────────
C_TITLE   = '#1A237E'   # dark navy
C_STEP    = '#1565C0'   # medium blue  (process)
C_DIAG    = '#283593'   # darker blue  (diagnostic section header)
C_MANIA   = '#B71C1C'   # deep red     (mania branch)
C_DEPRES  = '#1B5E20'   # deep green   (depression branch)
C_MIXED   = '#E65100'   # deep orange  (mixed)
C_MAINT   = '#4A148C'   # deep purple  (maintenance)
C_SPECIAL = '#006064'   # teal         (special situations)
C_DECI    = '#F57F17'   # amber        (decision diamond)
C_TEXT    = '#FAFAFA'
C_WARN    = '#FF8F00'
C_LIGHT   = '#EDE7F6'

# ── helper functions ─────────────────────────────────────────────────────────

def rounded_box(ax, x, y, w, h, color, text, fontsize=8.5, text_color='white',
                alpha=1.0, style='round,pad=0.05', bold=False, wrap_width=None):
    box = FancyBboxPatch((x - w/2, y - h/2), w, h,
                         boxstyle=style,
                         linewidth=1.2, edgecolor='white',
                         facecolor=color, alpha=alpha, zorder=3)
    ax.add_patch(box)
    weight = 'bold' if bold else 'normal'
    ax.text(x, y, text, ha='center', va='center', fontsize=fontsize,
            color=text_color, weight=weight,
            wrap=True, multialignment='center', zorder=4,
            linespacing=1.35)

def diamond(ax, x, y, w, h, color, text, fontsize=7.8):
    dx, dy = w/2, h/2
    pts = np.array([[x, y+dy], [x+dx, y], [x, y-dy], [x-dx, y]])
    poly = plt.Polygon(pts, closed=True, facecolor=color,
                       edgecolor='white', linewidth=1.2, zorder=3)
    ax.add_patch(poly)
    ax.text(x, y, text, ha='center', va='center', fontsize=fontsize,
            color='white', weight='bold', multialignment='center', zorder=4,
            linespacing=1.3)

def arrow(ax, x1, y1, x2, y2, color='#455A64', label='', lw=1.6):
    ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
                arrowprops=dict(arrowstyle='->', color=color,
                                lw=lw, connectionstyle='arc3,rad=0.0'),
                zorder=2)
    if label:
        mx, my = (x1+x2)/2, (y1+y2)/2
        ax.text(mx+0.15, my, label, fontsize=7, color=color, weight='bold', zorder=5)

def section_label(ax, x, y, text, color):
    ax.text(x, y, text, ha='center', va='center', fontsize=9,
            color=color, weight='bold', style='italic', zorder=5)

# ════════════════════════════════════════════════════════════════════════════
#  TITLE
# ════════════════════════════════════════════════════════════════════════════
title_box = FancyBboxPatch((0.3, 42.3), 25.4, 1.4, boxstyle='round,pad=0.1',
                            facecolor=C_TITLE, edgecolor='none', zorder=3)
ax.add_patch(title_box)
ax.text(13, 43.15, 'DIAGNOSIS & MANAGEMENT OF BIPOLAR DISORDER',
        ha='center', va='center', fontsize=15, color='white', weight='bold', zorder=4)
ax.text(13, 42.62, 'Based on DSM-5-TR | CANMAT/ISBD 2023 | NICE 2025 | VA/DoD 2023 Guidelines',
        ha='center', va='center', fontsize=8, color='#B3C8E8', zorder=4)

# ════════════════════════════════════════════════════════════════════════════
#  STEP 1 — INITIAL PRESENTATION & SCREENING
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 41.5, 16, 0.75, C_STEP,
            'STEP 1 — INITIAL PRESENTATION', fontsize=10, bold=True)

rounded_box(ax, 13, 40.6, 20, 0.95, '#37474F',
            'Patient presents with: mood episodes (elevated/irritable/depressed) • sleep disturbance • energy changes\n'
            'impulsive/risky behaviour • psychotic features • functional impairment',
            fontsize=7.8, alpha=0.95)
arrow(ax, 13, 40.12, 13, 39.55)

# ════════════════════════════════════════════════════════════════════════════
#  STEP 2 — SCREENING TOOLS
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 39.2, 16, 0.65, C_STEP,
            'STEP 2 — SCREENING', fontsize=10, bold=True)
rounded_box(ax, 13, 38.45, 20, 0.9,  '#37474F',
            'Validated tools: Mood Disorder Questionnaire (MDQ) | Bipolar Spectrum Diagnostic Scale (BSDS)\n'
            'Especially in patients with recurrent depression or poor antidepressant response',
            fontsize=7.8)
arrow(ax, 13, 37.99, 13, 37.42)

# ════════════════════════════════════════════════════════════════════════════
#  STEP 3 — FULL PSYCHIATRIC ASSESSMENT
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 37.1, 16, 0.65, C_STEP,
            'STEP 3 — COMPREHENSIVE PSYCHIATRIC ASSESSMENT', fontsize=9.5, bold=True)

rounded_box(ax, 5.5, 35.95, 9.5, 1.35, '#37474F',
            'History: current & past mood episodes (duration, severity)\n'
            'Psychiatric & family history (bipolarity in 1st-degree relatives)\n'
            'Collateral from family/caregivers | Mood charting\n'
            'Substance/medication use history', fontsize=7.5)

rounded_box(ax, 13, 35.95, 5.5, 1.35, '#37474F',
            'Exclude organic causes:\nHead trauma, stroke, epilepsy\nThyroid/electrolyte disease\n'
            'Drug-induced mania\n(corticosteroids, stimulants)', fontsize=7.5)

rounded_box(ax, 20.5, 35.95, 9.5, 1.35, '#37474F',
            'Investigations:\nTFTs, LFTs, renal function, FBC\nUrine drug screen\n'
            'ECG (if antipsychotic planned)\nNeuroimaging if atypical features', fontsize=7.5)

for xb in [5.5, 13, 20.5]:
    arrow(ax, xb, 37.27+0.3*0, xb, 36.28+0.35, color='#78909C')
arrow(ax, 5.5, 35.27, 9.5, 35.27, color='#78909C')
arrow(ax, 20.5, 35.27, 16.5, 35.27, color='#78909C')
arrow(ax, 13, 35.27, 13, 34.77)

# ════════════════════════════════════════════════════════════════════════════
#  STEP 4 — APPLY DSM-5-TR DIAGNOSTIC CRITERIA
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 34.45, 16, 0.65, C_DIAG,
            'STEP 4 — APPLY DSM-5-TR DIAGNOSTIC CRITERIA', fontsize=9.5, bold=True)

# Manic criteria box
rounded_box(ax, 5.2, 32.95, 9.8, 2.35, '#C62828',
            'MANIC EPISODE (BD-I)\n'
            '≥7 days (or any duration if hospitalised)\n'
            'Abnormally elevated/expansive/irritable mood\n'
            '+ increased energy/activity PLUS ≥3 of:\n'
            '• Grandiosity  • Decreased sleep need\n'
            '• Pressured speech  • Racing thoughts\n'
            '• Distractibility  • Psychomotor agitation\n'
            '• Risky/pleasurable activities with poor judgement\n'
            'Causes marked impairment / needs hospitalisation',
            fontsize=7.4, bold=False)

# Hypomanic criteria box
rounded_box(ax, 13, 32.95, 5.5, 2.35, '#558B2F',
            'HYPOMANIC EPISODE\n(BD-II)\n'
            '≥4 days, same criteria\nas mania BUT:\n'
            '• No marked impairment\n• No hospitalisation\n'
            '• No psychosis\n'
            '+ ≥1 MDE required\nfor BD-II diagnosis',
            fontsize=7.4)

# MDE criteria box
rounded_box(ax, 20.5, 32.95, 9.8, 2.35, '#1B5E20',
            'MAJOR DEPRESSIVE EPISODE\n'
            '≥2 weeks: ≥5 of:\n'
            '• Depressed mood (most of day)\n'
            '• Anhedonia  • Weight/appetite change\n'
            '• Insomnia or hypersomnia\n'
            '• Psychomotor change  • Fatigue\n'
            '• Worthlessness/guilt  • Poor concentration\n'
            '• Suicidal ideation / attempts\n'
            'At least 1 symptom = depressed mood or anhedonia',
            fontsize=7.4)

for xb in [5.2, 13, 20.5]:
    arrow(ax, xb, 34.12, xb, 34.12, color='#78909C')
    arrow(ax, 13, 34.12, xb, 34.12, color='#78909C', lw=1.2)

ax.annotate('', xy=(5.2, 34.12), xytext=(13, 34.12),
            arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.2), zorder=2)
ax.annotate('', xy=(20.5, 34.12), xytext=(13, 34.12),
            arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.2), zorder=2)
arrow(ax, 13, 34.12, 13, 34.12)

# draw three arrows down from step 4 to the boxes
arrow(ax, 5.2, 34.12, 5.2, 34.12)
for xb, ystart in [(5.2, 34.12), (13, 34.12), (20.5, 34.12)]:
    ax.annotate('', xy=(xb, 34.12), xytext=(13, 34.12),
                arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.2), zorder=2)

# straight down from step 4 header to the three boxes
ax.annotate('', xy=(5.2, 34.12), xytext=(5.2, 34.12), zorder=2)

# Re-draw arrows cleanly: from step4 bottom to each criteria box
step4_y = 34.12
for xb, ytop in [(5.2, 34.12), (13, 34.12), (20.5, 34.12)]:
    pass  # handled below with a horizontal line

# Horizontal connector at y=34.12
ax.plot([5.2, 20.5], [34.12, 34.12], color='#78909C', lw=1.5, zorder=2)
for xb in [5.2, 13, 20.5]:
    arrow(ax, xb, 34.12, xb, 34.12)

# Proper arrows from horizontal line down to each box
for xb, ybot in [(5.2, 34.12), (13, 34.12), (20.5, 34.12)]:
    ax.annotate('', xy=(xb, 34.12), xytext=(xb, 34.45),
                arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)

arrow(ax, 13, 34.12, 13, 34.12)

# arrows from step4 down to horizontal line, then down to boxes
ax.annotate('', xy=(13, 34.12), xytext=(13, 34.45-0.33),
            arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)
ax.plot([5.2, 20.5], [34.12, 34.12], color='#78909C', lw=1.6, zorder=2)
for xb in [5.2, 13, 20.5]:
    ax.annotate('', xy=(xb, 34.12), xytext=(xb, 34.12),
                arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)
    ax.annotate('', xy=(xb, 34.127), xytext=(xb, 34.11),
                arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)

# SIMPLER arrows: straight from 34.12 down to box tops
for xb in [5.2, 13, 20.5]:
    ax.annotate('', xy=(xb, 34.12), xytext=(xb, 34.45),
                arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)

# from horizontal bar down to each box top
for xb in [5.2, 13, 20.5]:
    ax.annotate('', xy=(xb, 34.12-0.001), xytext=(xb, 34.12+0.001),
                arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)

# ════════════════════════════════════════════════════════════════════════════
#  DIFFERENTIAL DIAGNOSIS band
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 31.3, 24, 0.85, '#4E342E',
            'DIFFERENTIAL DIAGNOSIS — Exclude before confirming BD:\n'
            'MDD (no mania/hypomania) | Schizophrenia/SZA | Substance-induced mood disorder | '
            'ADHD | BPD | Secondary mania (neurological/medical/iatrogenic) | Cyclothymia',
            fontsize=7.5)

for xb in [5.2, 13, 20.5]:
    arrow(ax, xb, 31.77, xb, 31.73)

# merge arrows to diff dx
ax.plot([5.2, 20.5], [31.77, 31.77], color='#78909C', lw=1.4, zorder=2)
ax.annotate('', xy=(13, 31.72), xytext=(13, 31.77),
            arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)

arrow(ax, 13, 30.87, 13, 30.37)

# ════════════════════════════════════════════════════════════════════════════
#  DIAGNOSIS CONFIRMED DIAMOND
# ════════════════════════════════════════════════════════════════════════════
diamond(ax, 13, 29.9, 7, 0.85, C_DECI, 'DIAGNOSIS CONFIRMED?', fontsize=9)
ax.text(17.3, 29.9, 'NO → Re-evaluate / refer', fontsize=7.5, color='#E65100',
        weight='bold', va='center', zorder=5)
ax.annotate('', xy=(16.52, 29.9), xytext=(16.52, 29.9), zorder=2)

arrow(ax, 13, 29.47, 13, 28.95)

# ════════════════════════════════════════════════════════════════════════════
#  BD TYPE ASSIGNMENT + RISK ASSESSMENT
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 28.65, 16, 0.55, C_DIAG,
            'STEP 5 — ASSIGN BD SUBTYPE & RISK ASSESSMENT', fontsize=9.5, bold=True)

rounded_box(ax, 5.5, 27.8, 9.5, 1.05, '#4527A0',
            'BD Subtype Assignment:\n'
            'BD-I: ≥1 manic episode (± MDE, ± psychosis)\n'
            'BD-II: hypomania + MDE (no full mania ever)\n'
            'Cyclothymia: 2yr hypomanic + depressive sx < threshold\n'
            'Specifiers: mixed, rapid cycling, peripartum, seasonal',
            fontsize=7.3)

rounded_box(ax, 20.5, 27.8, 9.5, 1.05, '#B71C1C',
            'Suicide / Safety Risk Assessment (MANDATORY):\n'
            'Suicidal ideation, plan, intent, access to means\n'
            'Hospitalisation if imminent risk\n'
            'Agitation, psychosis, self-harm, harm-to-others assessment',
            fontsize=7.3)

ax.plot([5.5, 20.5], [28.37, 28.37], color='#78909C', lw=1.4, zorder=2)
for xb in [5.5, 20.5]:
    ax.annotate('', xy=(xb, 28.37), xytext=(13, 28.37),
                arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.3), zorder=2)
ax.annotate('', xy=(13, 28.37), xytext=(13, 28.37+0.31),
            arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.3), zorder=2)

for xb in [5.5, 20.5]:
    arrow(ax, xb, 27.27, xb, 27.27)

ax.plot([5.5, 20.5], [27.27, 27.27], color='#78909C', lw=1.4, zorder=2)
ax.annotate('', xy=(13, 27.27), xytext=(13, 27.32),
            arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)

arrow(ax, 13, 27.27, 13, 26.75)

# ════════════════════════════════════════════════════════════════════════════
#  CURRENT PHASE DIAMOND
# ════════════════════════════════════════════════════════════════════════════
diamond(ax, 13, 26.3, 8.5, 0.95, C_DECI,
        'IDENTIFY CURRENT PHASE OF ILLNESS', fontsize=9)
arrow(ax, 13, 25.82, 13, 25.32)

ax.plot([4.5, 21.5], [25.32, 25.32], color='#455A64', lw=1.8, zorder=2)

# Labels above the three branch points
for xb, label in [(4.5, 'MANIC / HYPOMANIC'), (13, 'MIXED EPISODE'), (21.5, 'DEPRESSIVE')]:
    ax.text(xb, 25.52, label, ha='center', fontsize=7.5,
            color='white' if label=='MIXED EPISODE' else '#455A64',
            weight='bold', zorder=5,
            bbox=dict(facecolor=C_MANIA if 'MANIC' in label else
                                C_MIXED if 'MIXED' in label else C_DEPRES,
                      edgecolor='none', boxstyle='round,pad=0.2', alpha=0.9))

for xb in [4.5, 13, 21.5]:
    ax.annotate('', xy=(xb, 25.32), xytext=(xb, 25.47),
                arrowprops=dict(arrowstyle='->', color='#455A64', lw=1.6), zorder=2)

# ════════════════════════════════════════════════════════════════════════════
#  THREE TREATMENT BRANCHES
# ════════════════════════════════════════════════════════════════════════════

# ── MANIA BRANCH (left) ──────────────────────────────────────────────────
rounded_box(ax, 4.5, 24.55, 8.8, 0.65, C_MANIA,
            'ACUTE MANIA / HYPOMANIA MANAGEMENT', fontsize=8, bold=True)

rounded_box(ax, 4.5, 23.55, 8.8, 1.35, '#C62828',
            '1st LINE (BD-I Mania):\n'
            'Lithium OR Divalproex/Valproate OR\n'
            'Atypical antipsychotic monotherapy:\n'
            'Risperidone | Quetiapine | Aripiprazole\n'
            'Asenapine | Cariprazine | Olanzapine\n'
            'Consider combination if severe/psychotic',
            fontsize=7.2)

rounded_box(ax, 4.5, 22.25, 8.8, 0.95, '#E53935',
            '2nd LINE: Ziprasidone | Haloperidol\n'
            'Carbamazepine | Paliperidone\n'
            'Add benzodiazepine (short-term) for agitation',
            fontsize=7.2)

rounded_box(ax, 4.5, 21.4, 8.8, 0.65, '#FF7043',
            'ECT: severe/refractory mania, catatonia,\ndelirious mania, extreme agitation',
            fontsize=7.2)

rounded_box(ax, 4.5, 20.7, 8.8, 0.55, '#BF360C',
            'STOP antidepressants | Minimise stimulants',
            fontsize=7.2)

for y1, y2 in [(24.22, 24.22), (23.22, 22.72), (21.92, 21.72), (21.07, 20.97)]:
    pass

# arrows within mania column
for ytop, ybot in [(24.22, 23.22), (22.77, 21.72), (21.07, 20.97)]:
    ax.annotate('', xy=(4.5, ybot), xytext=(4.5, ytop),
                arrowprops=dict(arrowstyle='->', color=C_MANIA, lw=1.3), zorder=2)

# ── MIXED EPISODE BRANCH (centre) ────────────────────────────────────────
rounded_box(ax, 13, 24.55, 8.8, 0.65, C_MIXED,
            'MIXED EPISODE MANAGEMENT', fontsize=8, bold=True)

rounded_box(ax, 13, 23.6, 8.8, 1.2, '#E65100',
            '1st LINE:\n'
            'Divalproex/Valproate (preferred over lithium)\n'
            'Atypical antipsychotic:\n'
            'Olanzapine | Quetiapine | Aripiprazole | Asenapine\n'
            'Cariprazine (evidence for mixed features)',
            fontsize=7.2)

rounded_box(ax, 13, 22.4, 8.8, 0.8, '#EF6C00',
            '2nd LINE: Carbamazepine | Ziprasidone\n'
            'Lithium (less effective for mixed)\n'
            'Short-term BZD for acute distress',
            fontsize=7.2)

rounded_box(ax, 13, 21.65, 8.8, 0.55, '#FF8F00',
            'AVOID antidepressants (worsen mixed features)\nAVOID monotherapy lithium if dysphoric',
            fontsize=7.2, text_color='white')

for ytop, ybot in [(24.22, 24.19), (23.0, 22.8), (22.0, 21.93)]:
    ax.annotate('', xy=(13, ybot), xytext=(13, ytop),
                arrowprops=dict(arrowstyle='->', color=C_MIXED, lw=1.3), zorder=2)

# ── DEPRESSION BRANCH (right) ────────────────────────────────────────────
rounded_box(ax, 21.5, 24.55, 8.8, 0.65, C_DEPRES,
            'ACUTE BIPOLAR DEPRESSION MANAGEMENT', fontsize=8, bold=True)

rounded_box(ax, 21.5, 23.55, 8.8, 1.35, '#1B5E20',
            '1st LINE (BD-I & BD-II):\n'
            'Quetiapine (XR) monotherapy\n'
            'Lithium + Lamotrigine\n'
            'Divalproex | Lurasidone (± lithium/divalproex)\n'
            'Cariprazine (BD-I depression — FDA approved)\n'
            'Lumateperone (BD-I & II — FDA approved 2021)',
            fontsize=7.2)

rounded_box(ax, 21.5, 22.2, 8.8, 1.0, '#2E7D32',
            '2nd LINE: Olanzapine-fluoxetine combination\n'
            'Lamotrigine monotherapy | Lithium monotherapy\n'
            'Antidepressant + mood stabilizer (if no mixed/rapid)\n'
            '(SSRI/SNRI — add only to mood stabilizer backbone)',
            fontsize=7.2)

rounded_box(ax, 21.5, 21.35, 8.8, 0.6, '#33691E',
            'CAUTION: Antidepressants alone — risk of manic switch\nMonitor closely for emergent hypomania/mania',
            fontsize=7.2)

rounded_box(ax, 21.5, 20.7, 8.8, 0.45, '#558B2F',
            'ECT: severe depression, suicidality, catatonia, pregnancy',
            fontsize=7.2)

for ytop, ybot in [(24.22, 24.19), (22.87, 22.7), (21.7, 21.65), (21.07, 20.92)]:
    ax.annotate('', xy=(21.5, ybot), xytext=(21.5, ytop),
                arrowprops=dict(arrowstyle='->', color=C_DEPRES, lw=1.3), zorder=2)

# ── Converge to response assessment ─────────────────────────────────────
# Bottom of three branches → response assessment
for xb, ybot in [(4.5, 20.37+0.3), (13, 21.37), (21.5, 20.42+0.25)]:
    pass

ax.plot([4.5, 21.5], [20.25, 20.25], color='#455A64', lw=1.8, zorder=2)
for xb, ytop in [(4.5, 20.47), (13, 21.37), (21.5, 20.47)]:
    ax.annotate('', xy=(xb, 20.25), xytext=(xb, ytop),
                arrowprops=dict(arrowstyle='->', color='#455A64', lw=1.4), zorder=2)
ax.annotate('', xy=(13, 20.25), xytext=(13, 20.3),
            arrowprops=dict(arrowstyle='->', color='#455A64', lw=1.4), zorder=2)

# ════════════════════════════════════════════════════════════════════════════
#  RESPONSE ASSESSMENT DIAMOND
# ════════════════════════════════════════════════════════════════════════════
arrow(ax, 13, 20.25, 13, 19.8)
diamond(ax, 13, 19.35, 8.5, 0.85, C_DECI,
        'ASSESS RESPONSE AT 2–4 WEEKS', fontsize=9)

# Adequate response path
ax.text(12.5, 18.78, '✓ Adequate response', fontsize=7.5, color='#1B5E20',
        weight='bold', ha='center', zorder=5)
arrow(ax, 13, 18.92, 13, 18.42)

# No response path (right)
rounded_box(ax, 20, 19.35, 8.5, 0.85, '#B71C1C',
            'INADEQUATE RESPONSE / POOR TOLERABILITY:\n'
            'Review diagnosis & adherence | Check drug levels\n'
            'Exclude comorbidity (thyroid, substance use, personality disorder)\n'
            'Optimise dose → Switch to 2nd-line → Combination therapy\n'
            'Consider LAI (long-acting injectable) for adherence issues',
            fontsize=7.2)
ax.annotate('', xy=(16.52, 19.35), xytext=(16.52, 19.35),
            arrowprops=dict(arrowstyle='->', color='#B71C1C', lw=1.3), zorder=2)
ax.annotate('', xy=(15.77, 19.35), xytext=(16.52, 19.35),
            arrowprops=dict(arrowstyle='->', color='#B71C1C', lw=1.3), zorder=2)

# ════════════════════════════════════════════════════════════════════════════
#  MAINTENANCE TREATMENT
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 18.1, 16, 0.6, C_MAINT,
            'STEP 6 — MAINTENANCE / RELAPSE PREVENTION', fontsize=9.5, bold=True)

rounded_box(ax, 5.5, 17.05, 9.5, 1.2, '#6A1B9A',
            '1st LINE Maintenance Agents:\n'
            'Lithium (gold standard — antisuicidal effect)\n'
            'Divalproex/Valproate | Quetiapine\n'
            'Aripiprazole | Lamotrigine (depressive relapse)\n'
            'Asenapine | Cariprazine (adjunctive)',
            fontsize=7.3)

rounded_box(ax, 20.5, 17.05, 9.5, 1.2, '#7B1FA2',
            '2nd LINE Maintenance:\n'
            'Olanzapine | Carbamazepine\n'
            'Ziprasidone (adjunctive) | Risperidone\n'
            'Combination: Lithium + Valproate (superior)\n'
            'LAI (risperidone, aripiprazole) if adherence issues',
            fontsize=7.3)

ax.plot([5.5, 20.5], [17.82, 17.82], color='#78909C', lw=1.4, zorder=2)
for xb in [5.5, 20.5]:
    ax.annotate('', xy=(xb, 17.82), xytext=(13, 17.82),
                arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.3), zorder=2)
ax.annotate('', xy=(13, 17.82), xytext=(13, 17.82+0.28),
            arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.3), zorder=2)

for xb in [5.5, 20.5]:
    arrow(ax, xb, 16.45, xb, 16.45)

ax.plot([5.5, 20.5], [16.45, 16.45], color='#78909C', lw=1.4, zorder=2)
ax.annotate('', xy=(13, 16.45), xytext=(13, 16.5),
            arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)
arrow(ax, 13, 16.45, 13, 15.92)

# Duration note
rounded_box(ax, 13, 15.65, 18, 0.5, '#4527A0',
            'DURATION: Continue maintenance ≥2 years after first episode | ≥5 years after second episode | '
            'Often lifelong after 3+ episodes or high-risk features',
            fontsize=7.5)
arrow(ax, 13, 15.39, 13, 14.87)

# ════════════════════════════════════════════════════════════════════════════
#  PSYCHOSOCIAL & PSYCHOEDUCATION
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 14.6, 16, 0.55, C_STEP,
            'STEP 7 — PSYCHOSOCIAL INTERVENTIONS (ALL PHASES)', fontsize=9.5, bold=True)

rounded_box(ax, 6, 13.7, 10.5, 1.15, '#1565C0',
            'Psychoeducation (patient & family):\n'
            'Illness nature, triggers, early warning signs\n'
            'Adherence to medication | Lifestyle regularity\n'
            'Sleep hygiene | Substance avoidance',
            fontsize=7.4)

rounded_box(ax, 20, 13.7, 10.5, 1.15, '#1565C0',
            'Evidence-based psychotherapy:\n'
            'CBT | Interpersonal & Social Rhythm Therapy (IPSRT)\n'
            'Family-focused therapy | DBT (mixed features/BPD)\n'
            'Mindfulness-based cognitive therapy (MBCT)',
            fontsize=7.4)

ax.plot([6, 20], [14.32, 14.32], color='#78909C', lw=1.4, zorder=2)
for xb in [6, 20]:
    ax.annotate('', xy=(xb, 14.32), xytext=(13, 14.32),
                arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.3), zorder=2)
ax.annotate('', xy=(13, 14.32), xytext=(13, 14.32+0.28),
            arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.3), zorder=2)

for xb in [6, 20]:
    arrow(ax, xb, 13.12, xb, 13.12)
ax.plot([6, 20], [13.12, 13.12], color='#78909C', lw=1.4, zorder=2)
ax.annotate('', xy=(13, 13.12), xytext=(13, 13.17),
            arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)
arrow(ax, 13, 13.12, 13, 12.62)

# ════════════════════════════════════════════════════════════════════════════
#  MONITORING
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 12.35, 16, 0.55, C_STEP,
            'STEP 8 — ONGOING MONITORING', fontsize=9.5, bold=True)

rounded_box(ax, 13, 11.55, 22, 1.15, '#37474F',
            'Regular follow-up every 1–3 months (stable) | Every 1–4 weeks (acute phase)\n'
            'Lithium: serum levels + renal function + TFTs every 6 months\n'
            'Valproate: LFTs + FBC + drug levels | Carbamazepine: FBC + LFTs + drug levels\n'
            'Atypical antipsychotics: metabolic monitoring (weight, glucose, lipids) every 3–6 months\n'
            'Mood charting | Suicide risk reassessment at each visit',
            fontsize=7.4)
arrow(ax, 13, 12.07, 13, 11.12+0.0)
arrow(ax, 13, 10.97, 13, 10.47)

# ════════════════════════════════════════════════════════════════════════════
#  SPECIAL SITUATIONS
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 10.2, 16, 0.55, C_SPECIAL,
            'STEP 9 — SPECIAL SITUATIONS & COMORBIDITIES', fontsize=9.5, bold=True)

rounded_box(ax, 4.5, 9.2, 8.5, 1.3, '#006064',
            'PREGNANCY / PERIPARTUM:\n'
            'Avoid valproate (teratogenic)\n'
            'Lithium: cautious use (Ebstein risk low but real)\n'
            'Lamotrigine: preferred for depression maintenance\n'
            'Quetiapine/Olanzapine: acceptable if needed\n'
            'ECT: safe option for severe episodes',
            fontsize=7.2)

rounded_box(ax, 13, 9.2, 8.5, 1.3, '#00695C',
            'RAPID CYCLING (≥4 episodes/year):\n'
            'Optimise mood stabiliser\n'
            'Valproate preferred (better than Li alone)\n'
            'Thyroid/substance comorbidity check\n'
            'AVOID antidepressants\n'
            'Combination mood stabiliser therapy',
            fontsize=7.2)

rounded_box(ax, 21.5, 9.2, 8.5, 1.3, '#004D40',
            'COMORBIDITIES:\n'
            'Anxiety: quetiapine, lamotrigine preferred\n'
            'Substance use disorder: address concurrently\n'
            'ADHD: cautious stimulant use (+ mood stabiliser)\n'
            'Metabolic syndrome: favour agents with low\n'
            'metabolic risk (aripiprazole, lamotrigine)',
            fontsize=7.2)

ax.plot([4.5, 21.5], [9.92, 9.92], color='#78909C', lw=1.4, zorder=2)
for xb in [4.5, 13, 21.5]:
    ax.annotate('', xy=(xb, 9.92), xytext=(13, 9.92),
                arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.2), zorder=2)
ax.annotate('', xy=(13, 9.92), xytext=(13, 9.92+0.28),
            arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.2), zorder=2)

for xb in [4.5, 13, 21.5]:
    arrow(ax, xb, 8.55, xb, 8.55)
ax.plot([4.5, 21.5], [8.55, 8.55], color='#78909C', lw=1.4, zorder=2)
ax.annotate('', xy=(13, 8.55), xytext=(13, 8.6),
            arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)
arrow(ax, 13, 8.55, 13, 8.05)

# ════════════════════════════════════════════════════════════════════════════
#  TREATMENT RESISTANCE
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 7.8, 16, 0.55, '#B71C1C',
            'TREATMENT RESISTANCE PATHWAY', fontsize=9.5, bold=True)

rounded_box(ax, 13, 7.05, 22, 1.0, '#C62828',
            '1. Confirm diagnosis | 2. Confirm adherence | 3. Optimise serum drug levels\n'
            '4. Exclude organic/comorbid causes | 5. Clozapine (BD-I refractory mania/mixed)\n'
            '6. ECT (any refractory phase) | 7. Consider rTMS (depression) | '
            '8. Multidisciplinary team review | 9. Ketamine (investigational for refractory depression)',
            fontsize=7.3)
arrow(ax, 13, 7.52, 13, 6.55)
arrow(ax, 13, 6.54, 13, 6.04)

# ════════════════════════════════════════════════════════════════════════════
#  CHRONIC DISEASE MODEL BOX
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 5.75, 22, 0.9, '#1A237E',
            'CHRONIC DISEASE MANAGEMENT MODEL (BD is a lifelong illness):\n'
            'Shared decision-making | Patient-centred care | Peer support | Care coordination\n'
            'Advanced care directives | Regular review of wellbeing, functioning & quality of life',
            fontsize=7.5)
arrow(ax, 13, 5.29, 13, 4.79)

# ════════════════════════════════════════════════════════════════════════════
#  CONTINUOUS MONITORING LOOP ARROW (back to step 8)
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 4.5, 14, 0.55, '#263238',
            'Symptom recurrence / episode detected → Return to Phase-Specific Acute Treatment',
            fontsize=7.5)

ax.annotate('', xy=(0.8, 4.5), xytext=(6.0, 4.5),
            arrowprops=dict(arrowstyle='->', color='#263238', lw=1.4), zorder=2)
ax.annotate('', xy=(0.8, 26.3), xytext=(0.8, 4.5),
            arrowprops=dict(arrowstyle='->', color='#263238', lw=1.4, linestyle='dashed'), zorder=2)
ax.plot([0.8, 0.8], [4.5, 26.3], color='#263238', lw=1.4, linestyle='dashed', zorder=2)
ax.text(0.38, 15.4, 'RELAPSE', fontsize=7, color='#263238', weight='bold',
        rotation=90, va='center', zorder=5)
ax.text(0.38, 15.0, 'LOOP', fontsize=7, color='#263238', weight='bold',
        rotation=90, va='center', zorder=5)

# ════════════════════════════════════════════════════════════════════════════
#  LEGEND / KEY
# ════════════════════════════════════════════════════════════════════════════
legend_patches = [
    mpatches.Patch(color=C_MANIA,   label='Mania / Hypomania Branch'),
    mpatches.Patch(color=C_MIXED,   label='Mixed Episode Branch'),
    mpatches.Patch(color=C_DEPRES,  label='Depression Branch'),
    mpatches.Patch(color=C_MAINT,   label='Maintenance Therapy'),
    mpatches.Patch(color=C_SPECIAL, label='Special Situations'),
    mpatches.Patch(color=C_DECI,    label='Decision / Assessment'),
    mpatches.Patch(color=C_STEP,    label='Diagnostic / Management Step'),
    mpatches.Patch(color='#B71C1C', label='Treatment Resistance'),
]
ax.legend(handles=legend_patches, loc='lower right',
          bbox_to_anchor=(1.0, 0.0),
          fontsize=7, framealpha=0.9, title='Colour Key', title_fontsize=7.5,
          edgecolor='#455A64', facecolor='#ECEFF1')

# Footer
ax.text(13, 0.35,
        'Sources: DSM-5-TR (APA 2022) | CANMAT/ISBD Guidelines 2023 | NICE Bipolar Disorder Guidelines 2025 | VA/DoD CPG 2023 | Goldman-Cecil Medicine\n'
        'This flowchart is for educational use only — clinical judgement, local formulary, and patient preferences must guide individual decisions.',
        ha='center', va='center', fontsize=6.5, color='#607D8B', style='italic')

plt.tight_layout(pad=0.4)
plt.savefig('/home/daytona/workspace/bipolar-flowchart/bipolar_flowchart.png',
            dpi=170, bbox_inches='tight', facecolor='#F7F9FC')
print("Saved successfully.")
Running Command

cd /home/daytona/workspace/bipolar-flowchart && python bipolar_flowchart.py 2>&1

Loading Image
Running Command

ls -lh /home/daytona/workspace/bipolar-flowchart/bipolar_flowchart.png

The file is generated (1.4 MB). The fetch_image tool only works with HTTP URLs, not local file paths. The image is ready for download.

Here is your schematic flowchart:

bipolar flowchart

Image · PNG


What the Flowchart Covers

The chart is structured as a 9-step vertical algorithm with three parallel treatment branches and a relapse loop. Here is a summary of its content:

Diagnostic Steps (1–5)

StepContent
1Initial presentation (mood, sleep, energy, behaviour)
2Screening - MDQ and BSDS tools
3Full assessment - history, exclude organic causes, investigations (TFTs, renal, ECG, urine drug screen)
4DSM-5-TR criteria for manic episode (BD-I), hypomanic episode (BD-II), and MDE displayed side-by-side
5BD subtype assignment (I, II, cyclothymia, specifiers) + mandatory suicide/safety risk assessment

Treatment Branches (Phase-Specific)

Three parallel columns for:
  • Mania/Hypomania - lithium, valproate, or atypical antipsychotic monotherapy (risperidone, quetiapine, aripiprazole, asenapine, cariprazine); ECT for severe/refractory
  • Mixed Episodes - valproate preferred, avoid antidepressants and lithium monotherapy
  • Bipolar Depression - quetiapine XR, lurasidone, cariprazine, lumateperone (FDA approved 2021); antidepressants only with mood stabiliser backbone

Steps 6–9

  • Maintenance - lithium (gold standard, antisuicidal), valproate, quetiapine, lamotrigine; duration guidance
  • Psychosocial - psychoeducation, CBT, IPSRT, family-focused therapy, MBCT
  • Monitoring - lithium levels, metabolic monitoring, mood charting, suicide reassessment
  • Special situations - pregnancy (avoid valproate), rapid cycling, comorbidities
  • Treatment resistance - clozapine, ECT, rTMS, ketamine (investigational)
Sources: DSM-5-TR (APA 2022) | CANMAT/ISBD 2023 | NICE 2025 | VA/DoD CPG 2023 | Goldman-Cecil Medicine
This is a shared conversation. Sign in to Orris to start your own chat.