evaldoc 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/.env.staging.example +19 -0
  2. package/.firebaserc +6 -0
  3. package/.github/workflows/auto-merge-to-main.yml +28 -0
  4. package/.github/workflows/ci.yml +27 -0
  5. package/.github/workflows/npxhub-publish.yml +170 -0
  6. package/.prettierignore +4 -0
  7. package/.prettierrc.json +7 -0
  8. package/README.md +142 -0
  9. package/SRS.md +448 -0
  10. package/apphosting.staging.yaml +48 -0
  11. package/apphosting.yaml +50 -0
  12. package/bin/evaldoc.cjs +105 -0
  13. package/cors.json +8 -0
  14. package/docs/ARCHITECTURE.md +769 -0
  15. package/docs/ARCHITECTURE_SUMMARY.md +323 -0
  16. package/docs/DEPLOYMENT.md +86 -0
  17. package/docs/DEPLOYMENT_STEPS.md +373 -0
  18. package/docs/DEPLOY_FIRESTORE_RULES.md +239 -0
  19. package/docs/DEPLOY_STORAGE_RULES.md +82 -0
  20. package/docs/DOCTOR_TUTORIAL.md +467 -0
  21. package/docs/FIREBASE_STORAGE_CORS_FIX.md +131 -0
  22. package/docs/FIXES_APPLIED.md +128 -0
  23. package/docs/IMPLEMENTATION.md +498 -0
  24. package/docs/IMPLEMENTATION_COMPLETE.md +317 -0
  25. package/docs/LLM-SETUP.md +216 -0
  26. package/docs/MANUAL_TEST_GUIDE.md +327 -0
  27. package/docs/PATIENT_GUIDE.md +451 -0
  28. package/docs/PLATFORM_OVERVIEW.md +194 -0
  29. package/docs/QUICKSTART.md +252 -0
  30. package/docs/QUICK_REFERENCE.md +222 -0
  31. package/docs/READY_TO_TEST.md +383 -0
  32. package/docs/RUN.md +237 -0
  33. package/docs/START_TESTING.md +130 -0
  34. package/docs/STRIPE_CONNECT_SETUP.md +454 -0
  35. package/docs/SYSTEM_READY.md +345 -0
  36. package/docs/THREADS_IMPLEMENTATION_COMPLETE.md +431 -0
  37. package/docs/THREAD_IMPLEMENTATION_GUIDE.md +345 -0
  38. package/docs/firebasefiles.md +21 -0
  39. package/docs/homepage.md +1208 -0
  40. package/docs/payments.md +159 -0
  41. package/eslint.config.js +76 -0
  42. package/firebase.json +27 -0
  43. package/firestore.indexes.json +88 -0
  44. package/firestore.rules +125 -0
  45. package/functions/package-lock.json +2884 -0
  46. package/functions/package.json +20 -0
  47. package/functions/src/analytics.js +101 -0
  48. package/functions/src/audit.js +28 -0
  49. package/functions/src/auth.js +19 -0
  50. package/functions/src/drafts.js +128 -0
  51. package/functions/src/feedback.js +164 -0
  52. package/functions/src/firebase-init.js +29 -0
  53. package/functions/src/i18n.js +134 -0
  54. package/functions/src/limits.js +27 -0
  55. package/functions/src/llm.js +271 -0
  56. package/functions/src/ner.js +162 -0
  57. package/functions/src/notifications.js +273 -0
  58. package/functions/src/orgs.js +127 -0
  59. package/functions/src/patient-api.js +545 -0
  60. package/functions/src/payment.js +830 -0
  61. package/functions/src/safety.js +188 -0
  62. package/functions/src/stripe-config.js +33 -0
  63. package/functions/src/topics.js +94 -0
  64. package/functions/src/triage.js +191 -0
  65. package/functions/src/utils.js +28 -0
  66. package/functions/src/validate.js +177 -0
  67. package/functions/test-firebase.js +36 -0
  68. package/jest.config.js +18 -0
  69. package/package.json +52 -0
  70. package/public/404.html +93 -0
  71. package/public/app/analytics.html +336 -0
  72. package/public/app/analytics.js +177 -0
  73. package/public/app/app.css +1400 -0
  74. package/public/app/app.js +1754 -0
  75. package/public/app/config.js +14 -0
  76. package/public/app/inbox-threads.js +843 -0
  77. package/public/app/inbox.html +598 -0
  78. package/public/app/inbox.js +397 -0
  79. package/public/app/index.html +612 -0
  80. package/public/assets/logo.jpg +0 -0
  81. package/public/chat.html +137 -0
  82. package/public/css/chat.css +870 -0
  83. package/public/css/landing-enhance.css +298 -0
  84. package/public/css/styles.css +1198 -0
  85. package/public/favicon.svg +5 -0
  86. package/public/find-doctor.html +220 -0
  87. package/public/index.html +399 -0
  88. package/public/js/chat-threads.js +513 -0
  89. package/public/js/chat.js +832 -0
  90. package/public/js/search.js +41 -0
  91. package/public/privacy.html +101 -0
  92. package/public/robots.txt +6 -0
  93. package/public/site.webmanifest +12 -0
  94. package/public/terms.html +110 -0
  95. package/scripts/create-test-provider.js +55 -0
  96. package/scripts/deploy-staging.sh +51 -0
  97. package/scripts/fix-provider.js +39 -0
  98. package/scripts/quick-test.sh +37 -0
  99. package/scripts/test-chat.sh +54 -0
  100. package/scripts/test-multi-turn.js +137 -0
  101. package/scripts/test-ner-simple.js +28 -0
  102. package/scripts/test-ner.js +60 -0
  103. package/scripts/test-stripe-connect.js +304 -0
  104. package/scripts/test-webhook-secret.sh +24 -0
  105. package/server.js +3864 -0
  106. package/storage.rules +22 -0
  107. package/test-api.json +1 -0
  108. package/tests/emulator/README.md +38 -0
  109. package/tests/emulator/counters.emulator.test.js +81 -0
  110. package/tests/integration/api-account-selfserve.test.js +312 -0
  111. package/tests/integration/api-admin-digest.test.js +202 -0
  112. package/tests/integration/api-chat-satisfaction.test.js +259 -0
  113. package/tests/integration/api-doctor-drafts.test.js +236 -0
  114. package/tests/integration/api-doctor-inbox.test.js +401 -0
  115. package/tests/integration/api-org.test.js +328 -0
  116. package/tests/integration/api-payment.test.js +664 -0
  117. package/tests/integration/api-provider-settings.test.js +475 -0
  118. package/tests/integration/api-qr.test.js +121 -0
  119. package/tests/integration/api-security.test.js +241 -0
  120. package/tests/integration/api-stripe-search.test.js +303 -0
  121. package/tests/integration/api-webhook-deletion.test.js +231 -0
  122. package/tests/mocks/firebase-admin.js +220 -0
  123. package/tests/unit/analytics.test.js +151 -0
  124. package/tests/unit/audit-bugs.test.js +203 -0
  125. package/tests/unit/chat-ui-logic.test.js +283 -0
  126. package/tests/unit/drafts.test.js +104 -0
  127. package/tests/unit/feedback.test.js +148 -0
  128. package/tests/unit/i18n.test.js +229 -0
  129. package/tests/unit/llm-retry.test.js +122 -0
  130. package/tests/unit/notifications.test.js +155 -0
  131. package/tests/unit/orgs.test.js +328 -0
  132. package/tests/unit/payment.test.js +889 -0
  133. package/tests/unit/safety-edge-cases.test.js +509 -0
  134. package/tests/unit/safety-srs-compliance.test.js +252 -0
  135. package/tests/unit/safety.test.js +291 -0
  136. package/tests/unit/server-helpers.test.js +774 -0
  137. package/tests/unit/topics-limits.test.js +158 -0
  138. package/tests/unit/triage.test.js +152 -0
  139. package/tests/unit/validate.test.js +408 -0
@@ -0,0 +1,19 @@
1
+ # EvalDoc Staging Environment
2
+ # Copy to .env.staging and fill in real values
3
+
4
+ NODE_ENV=staging
5
+ PROJECT_ID=evaldocplatform-staging
6
+ PLATFORM_URL=https://staging.evaldoctor.ai
7
+ ALLOWED_ORIGINS=https://staging.evaldoctor.ai
8
+
9
+ # LLM Keys (same as production or separate)
10
+ GEMINI_API_KEY=
11
+ DEEPSEEK_API_KEY=
12
+
13
+ # Stripe TEST mode keys (pk_test_ / sk_test_)
14
+ STRIPE_PUBLISHABLE_KEY=pk_test_...
15
+ STRIPE_SECRET_KEY=sk_test_...
16
+ STRIPE_WEBHOOK_SECRET=whsec_...
17
+
18
+ # Firebase (download staging service account key)
19
+ GOOGLE_APPLICATION_CREDENTIALS=./serviceAccountKey-staging.json
package/.firebaserc ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "projects": {
3
+ "default": "evaldocplatform",
4
+ "staging": "evaldocplatform-staging"
5
+ }
6
+ }
@@ -0,0 +1,28 @@
1
+ name: Auto-merge claude branches to main
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - 'claude/**'
7
+
8
+ jobs:
9
+ merge-to-main:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: write
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ with:
16
+ fetch-depth: 0
17
+ token: ${{ secrets.GITHUB_TOKEN }}
18
+
19
+ - name: Configure git
20
+ run: |
21
+ git config user.name "github-actions[bot]"
22
+ git config user.email "github-actions[bot]@users.noreply.github.com"
23
+
24
+ - name: Merge to main
25
+ run: |
26
+ git checkout main
27
+ git merge origin/${{ github.ref_name }} --no-edit
28
+ git push origin main
@@ -0,0 +1,27 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ build:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - name: Checkout
12
+ uses: actions/checkout@v4
13
+
14
+ - name: Set up Node.js
15
+ uses: actions/setup-node@v4
16
+ with:
17
+ node-version: 20
18
+
19
+ - name: Install dependencies
20
+ run: npm ci
21
+
22
+ - name: Lint
23
+ run: npm run lint
24
+ continue-on-error: true
25
+
26
+ - name: Test
27
+ run: npm test
@@ -0,0 +1,170 @@
1
+ # Added by npxhub. Releases are started from the npxhub dashboard.
2
+ # npxhub-workflow: v3
3
+ name: npxhub publish
4
+ run-name: npxhub ${{ inputs.release_id }}
5
+
6
+ on:
7
+ workflow_dispatch:
8
+ inputs:
9
+ version: { description: "Version to publish", required: true, type: string }
10
+ tag: { description: "npm dist-tag", required: true, type: string, default: latest }
11
+ git_tag: { description: "Git tag to create", required: true, type: string }
12
+ directory: { description: "Package directory", required: false, type: string, default: "." }
13
+ notes: { description: "Release notes", required: false, type: string, default: "" }
14
+ provenance: { description: "Publish with provenance", required: false, type: boolean, default: false }
15
+ release_id: { description: "npxhub release id", required: true, type: string }
16
+ log_url: { description: "Where to send live step output", required: false, type: string, default: "" }
17
+ log_token: { description: "Token for log_url", required: false, type: string, default: "" }
18
+
19
+ permissions:
20
+ contents: write
21
+ id-token: write
22
+
23
+ concurrency:
24
+ group: npxhub-publish-${{ inputs.directory }}
25
+ cancel-in-progress: false
26
+
27
+ jobs:
28
+ publish:
29
+ name: npxhub publish
30
+ runs-on: ubuntu-latest
31
+ defaults:
32
+ run:
33
+ working-directory: ${{ inputs.directory }}
34
+ env:
35
+ VERSION: ${{ inputs.version }}
36
+ DIST_TAG: ${{ inputs.tag }}
37
+ GIT_TAG: ${{ inputs.git_tag }}
38
+ NOTES: ${{ inputs.notes }}
39
+ NPXHUB_LOG_URL: ${{ inputs.log_url }}
40
+ NPXHUB_LOG_TOKEN: ${{ inputs.log_token }}
41
+ steps:
42
+ - name: Checkout
43
+ uses: actions/checkout@v5
44
+ with:
45
+ fetch-depth: 0
46
+
47
+ - name: Set up Node
48
+ uses: actions/setup-node@v5
49
+ with:
50
+ node-version: 22
51
+ registry-url: https://registry.npmjs.org
52
+
53
+ - name: Set up npxhub log
54
+ run: |
55
+ if [ -n "$NPXHUB_LOG_TOKEN" ]; then echo "::add-mask::$NPXHUB_LOG_TOKEN"; fi
56
+ cat > /tmp/npxhub-log.cjs <<'NPXHUB_EOF'
57
+ "use strict"
58
+ const { spawn } = require("node:child_process")
59
+ const step = Number(process.argv[2])
60
+ const script = process.argv[3]
61
+ const url = process.env.NPXHUB_LOG_URL
62
+ const token = process.env.NPXHUB_LOG_TOKEN
63
+ const secrets = Object.keys(process.env)
64
+ .filter((k) => ["TOKEN", "SECRET", "PASSWORD", "KEY"].some((s) => k.toUpperCase().includes(s)))
65
+ .map((k) => process.env[k])
66
+ .filter((v) => v && v.length >= 8)
67
+ let pending = ""
68
+ let queue = Promise.resolve()
69
+ function redact(text) {
70
+ for (const s of secrets) text = text.split(s).join("***")
71
+ return text
72
+ }
73
+ function send(final) {
74
+ const cut = final ? pending.length : pending.lastIndexOf("\n") + 1
75
+ if (!url || cut === 0) return queue
76
+ const text = redact(pending.slice(0, cut))
77
+ pending = pending.slice(cut)
78
+ const body = JSON.stringify({ step: step, text: text })
79
+ const headers = { "content-type": "application/json", "x-npxhub-log-token": token }
80
+ queue = queue
81
+ .then(() => fetch(url, { method: "POST", headers: headers, body: body, signal: AbortSignal.timeout(10000) }))
82
+ .catch(() => {})
83
+ return queue
84
+ }
85
+ const timer = setInterval(() => send(false), 2000)
86
+ const child = spawn("bash", ["--noprofile", "--norc", "-eo", "pipefail", script], { stdio: ["inherit", "pipe", "pipe"] })
87
+ child.stdout.on("data", (d) => {
88
+ process.stdout.write(d)
89
+ pending += d
90
+ })
91
+ child.stderr.on("data", (d) => {
92
+ process.stderr.write(d)
93
+ pending += d
94
+ })
95
+ child.on("close", (code) => {
96
+ clearInterval(timer)
97
+ if (code !== 0) pending += "\n[step exited with code " + code + "]\n"
98
+ send(true).then(() => process.exit(code === null ? 1 : code))
99
+ })
100
+
101
+ NPXHUB_EOF
102
+
103
+ - name: Bump version and commit
104
+ shell: node /tmp/npxhub-log.cjs 2 {0}
105
+ run: |
106
+ git config user.name "github-actions[bot]"
107
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
108
+ npm version "$VERSION" --no-git-tag-version --allow-same-version
109
+ # A first release can equal the version already in package.json; then there is nothing to commit.
110
+ if git diff --quiet; then echo "package.json already at $VERSION"; else git commit -am "chore(release): $GIT_TAG"; fi
111
+
112
+ - name: Build and test
113
+ shell: node /tmp/npxhub-log.cjs 3 {0}
114
+ run: |
115
+ if [ -f package-lock.json ]; then npm ci
116
+ elif [ -f "$GITHUB_WORKSPACE/package-lock.json" ]; then (cd "$GITHUB_WORKSPACE" && npm ci)
117
+ else npm install; fi
118
+ npm run build --if-present
119
+ npm test --if-present
120
+ npm pack --dry-run --json > "$RUNNER_TEMP/pack.json"
121
+ node -e '
122
+ const files = require(process.env.RUNNER_TEMP + "/pack.json")[0].files.map(f => f.path)
123
+ // Templates such as .env.example or .env.staging.example are allowed; real env files and keys are not.
124
+ const secretLike = files.filter(p => /(^|\/)(\.env[^/]*|[^/]*\.pem|id_rsa[^/]*)$/.test(p))
125
+ const bad = secretLike.filter(p => !/\.(example|sample|template|dist|pub)$/.test(p))
126
+ if (secretLike.length > bad.length) console.log("Allowed templates: " + secretLike.filter(p => !bad.includes(p)).join(", "))
127
+ console.log(files.length + " files in tarball")
128
+ if (bad.length) { console.error("Blocked: secret-looking files in tarball: " + bad.join(", ")); process.exit(1) }
129
+ '
130
+
131
+ - name: Tag and GitHub release
132
+ shell: node /tmp/npxhub-log.cjs 4 {0}
133
+ env:
134
+ GH_TOKEN: ${{ github.token }}
135
+ run: |
136
+ # Safe to re-run after a failed release: reuse an existing tag and release.
137
+ if git rev-parse -q --verify "refs/tags/$GIT_TAG" > /dev/null; then
138
+ echo "Tag $GIT_TAG already exists"
139
+ git push origin "HEAD:$GITHUB_REF_NAME"
140
+ else
141
+ git tag -a "$GIT_TAG" -m "$GIT_TAG"
142
+ git push origin "HEAD:$GITHUB_REF_NAME" --follow-tags
143
+ fi
144
+ FLAGS=(--title "$GIT_TAG" --notes "$NOTES")
145
+ if [ "$DIST_TAG" != "latest" ]; then FLAGS+=(--prerelease); fi
146
+ if gh release view "$GIT_TAG" > /dev/null 2>&1; then echo "Release $GIT_TAG already exists"; else gh release create "$GIT_TAG" "${FLAGS[@]}"; fi
147
+
148
+ - name: Publish to npm
149
+ shell: node /tmp/npxhub-log.cjs 5 {0}
150
+ env:
151
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
152
+ PROVENANCE: ${{ inputs.provenance }}
153
+ run: |
154
+ if [ -z "$NODE_AUTH_TOKEN" ]; then echo "NPM_TOKEN secret is not set on this repo"; exit 1; fi
155
+ FLAGS=(--access public --tag "$DIST_TAG")
156
+ if [ "$PROVENANCE" = "true" ]; then FLAGS+=(--provenance); fi
157
+ npm publish "${FLAGS[@]}"
158
+
159
+ - name: Verify install
160
+ shell: node /tmp/npxhub-log.cjs 6 {0}
161
+ run: |
162
+ NAME=$(node -p "require('./package.json').name")
163
+ HAS_BIN=$(node -p "Boolean(require('./package.json').bin)")
164
+ for _ in $(seq 1 20); do npm view "$NAME@$VERSION" version > /dev/null 2>&1 && break; sleep 6; done
165
+ cd "$(mktemp -d)"
166
+ if [ "$HAS_BIN" = "true" ]; then
167
+ timeout 120 npx -y "$NAME@$VERSION" --version < /dev/null || timeout 120 npx -y "$NAME@$VERSION" --help < /dev/null
168
+ else
169
+ npm install "$NAME@$VERSION" --no-save
170
+ fi
@@ -0,0 +1,4 @@
1
+ node_modules
2
+ coverage
3
+ package-lock.json
4
+ functions
@@ -0,0 +1,7 @@
1
+ {
2
+ "singleQuote": true,
3
+ "semi": true,
4
+ "printWidth": 100,
5
+ "trailingComma": "none",
6
+ "arrowParens": "always"
7
+ }
package/README.md ADDED
@@ -0,0 +1,142 @@
1
+ # EvalDoc Platform
2
+
3
+ 🏥 AI-powered healthcare chat platform with medical safety guards
4
+
5
+ 🟡 **Status**: Experimental with Gemini 2.0 Flash & DeepSeek LLMs
6
+ ✅ **LLM Integration**: Working (Gemini 2.0 Flash + DeepSeek fallback)
7
+ ✅ **Safety Pipeline**: Multi-layer validation with NER, PII detection, escalation
8
+ ✅ **Homepage**: Patient-facing landing page with provider search
9
+
10
+ ## Quick Start - Local Development
11
+
12
+ ```bash
13
+ # Start the server
14
+ PORT=8080 node server.js
15
+
16
+ # Visit the app
17
+ http://localhost:8080/ # Homepage with provider search
18
+ http://localhost:8080/mollit-lorem-consequ # Test provider chat
19
+ http://localhost:8080/find-doctor.html # Provider directory
20
+ ```
21
+
22
+ 📖 **[Full Implementation Details →](IMPLEMENTATION.md)**
23
+
24
+ ---
25
+
26
+ ## Structure
27
+
28
+ ### Frontend (Patient-Facing)
29
+ - `public/index.html` - Landing page with hero & provider search
30
+ - `public/chat.html` - Chat interface (served via clean URLs: `/{handle}`)
31
+ - `public/find-doctor.html` - Full provider directory
32
+ - `public/css/` - Styles (homepage + chat-specific)
33
+ - `public/js/` - JavaScript (search + chat logic)
34
+
35
+ ### Backend
36
+ - `server.js` - Main API server with routing
37
+ - `functions/src/` - Backend modules
38
+ - `llm.js` - **Gemini 2.0 Flash + DeepSeek (improved prompts)**
39
+ - `safety.js` - Multi-layer safety pipeline
40
+ - `ner.js` - Medical Named Entity Recognition
41
+ - `topics.js` - 6 medical topics with controls
42
+ - `analytics.js` - Usage tracking
43
+ - `limits.js` - Rate limiting
44
+
45
+ ### Configuration
46
+ - `firestore.rules` - Security rules
47
+ - `firestore.indexes.json` - Database indexes
48
+ - `.env` - API keys (Gemini, DeepSeek, HuggingFace)
49
+ - `SRS.md` - Requirements specification
50
+ - `IMPLEMENTATION.md` - Technical documentation
51
+
52
+ ## Key Features ✅
53
+
54
+ ✅ **Homepage & Provider Discovery**: Landing page with real-time search
55
+ ✅ **Clean URLs**: Share providers via `/{handle}` instead of query params
56
+ ✅ **LLM Integration**: Gemini 2.0 Flash (primary) + DeepSeek (fallback)
57
+ ✅ **Improved Prompts**: Less restrictive, more helpful responses (250 tokens)
58
+ ✅ **Medical NER**: Entity detection with Hugging Face models
59
+ ✅ **Safety-First**: Multi-layer validation, PII redaction, red flag escalation
60
+ ✅ **Topic-Based**: 6 predefined medical topics with custom controls
61
+ ✅ **Anonymous Chat**: No patient login required
62
+ ✅ **Responsive Design**: Mobile, tablet, desktop optimized
63
+
64
+ ## API Endpoints
65
+
66
+ ### Public
67
+ - `GET /` - Homepage with provider search
68
+ - `GET /:handle` - Chat interface (clean URL routing)
69
+ - `GET /find-doctor.html` - Provider directory
70
+ - `GET /api/search-providers?q=query` - Search providers (name + specialty)
71
+ - `GET /api/provider?handle=<handle>` - Get provider profile
72
+ - `POST /api/chat` - Send message (anonymous, with safety pipeline)
73
+ - `POST /api/satisfaction` - Submit rating
74
+
75
+ ### Authenticated (Firebase Auth token required)
76
+ - `GET /api/me` - Current provider details
77
+ - `POST /api/provider` - Create provider
78
+ - `POST /api/provider/update` - Update settings
79
+ - `GET /api/conversations` - List conversations
80
+ - `GET /api/analytics` - Dashboard data
81
+
82
+ ## ✅ LLM Integration Status
83
+
84
+ **Working Implementation:**
85
+ - **Primary**: Gemini 2.0 Flash (`gemini-2.0-flash-exp`)
86
+ - **Fallback**: DeepSeek Chat
87
+ - **Response Quality**: Improved system prompts (more helpful, less guarded)
88
+ - **Token Limits**: 250 tokens per response
89
+ - **Temperature**: 0.4 (balanced between accuracy and creativity)
90
+
91
+ See [functions/src/llm.js](functions/src/llm.js) for implementation.
92
+
93
+ ## Medical Topics Supported
94
+
95
+ 1. `medication_info_safety` - Medication facts & warnings
96
+ 2. `appointments_preparation` - Appointment prep tips
97
+ 3. `general_health_info` - General health education
98
+ 4. `clinic_services_hours` - Clinic info & hours
99
+ 5. `insurance_billing_faq` - Billing questions
100
+ 6. `post_procedure_guidance` - Post-op care
101
+
102
+ Each topic has custom prompts, disclaimers, and safety rules.
103
+
104
+ ## Local Setup (Alternative)
105
+
106
+ **Note**: Firebase CLI has issues locally. Use online deployment instead.
107
+
108
+ ```bash
109
+ cd functions && npm install
110
+ firebase emulators:start --only functions,firestore,hosting
111
+ # Visit http://localhost:5000/app
112
+ ```
113
+
114
+ ## Production Checklist
115
+
116
+ Before launching:
117
+ - [ ] Integrate real LLM provider (OpenAI/Claude/Vertex)
118
+ - [ ] Restrict CORS to specific domains
119
+ - [ ] Add comprehensive logging
120
+ - [ ] Legal review of disclaimers
121
+ - [ ] HIPAA compliance review
122
+ - [ ] Test with real providers
123
+ - [ ] Set up monitoring & alerts
124
+ - [ ] Configure custom domain
125
+
126
+ ## Tech Stack
127
+
128
+ - **Frontend**: Vanilla JS, HTML, CSS
129
+ - **Backend**: Node.js 20, Firebase Cloud Functions
130
+ - **Database**: Firestore
131
+ - **Auth**: Firebase Authentication
132
+ - **Hosting**: Firebase Hosting
133
+
134
+ ## Documentation
135
+
136
+ - **[DEPLOYMENT.md](DEPLOYMENT.md)** - How to deploy
137
+ - **[SRS.md](SRS.md)** - Requirements spec
138
+ - **Firebase Project**: `evaldocplatform`
139
+
140
+ ---
141
+
142
+ **Next Steps**: Deploy to Firebase online → Integrate LLM → Test with providers