From c4ef090a20730398a0d18126fc3c0cab63dd3d20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Boschi?= Date: Fri, 6 Feb 2026 10:49:13 +0100 Subject: [PATCH] feat: support markdown in reflect and mental models (#307) * feat: support markdown in reflect and mental models * chore: regenerate clients and OpenAPI spec with markdown field descriptions --- ...s0t1u2v3_fix_mental_models_pk_isolation.py | 60 ++++++++++++++++ hindsight-api/hindsight_api/api/http.py | 14 ++-- .../engine/consolidation/prompts.py | 15 ++-- .../hindsight_api/engine/reflect/models.py | 2 +- .../hindsight_api/engine/reflect/prompts.py | 19 +++-- .../engine/reflect/tools_schema.py | 4 +- .../models/mental_model_response.py | 4 +- .../models/reflect_fact.py | 4 +- .../models/reflect_response.py | 4 +- .../typescript/generated/types.gen.ts | 6 ++ hindsight-control-plane/package.json | 1 + hindsight-control-plane/src/app/globals.css | 71 +++++++++++++++++++ .../src/components/bank-profile-view.tsx | 3 +- .../components/mental-model-detail-modal.tsx | 3 +- .../src/components/mental-models-view.tsx | 23 +++++- .../src/components/think-view.tsx | 14 ++-- hindsight-docs/static/openapi.json | 11 +-- package-lock.json | 5 +- 18 files changed, 226 insertions(+), 37 deletions(-) create mode 100644 hindsight-api/hindsight_api/alembic/versions/w8r9s0t1u2v3_fix_mental_models_pk_isolation.py diff --git a/hindsight-api/hindsight_api/alembic/versions/w8r9s0t1u2v3_fix_mental_models_pk_isolation.py b/hindsight-api/hindsight_api/alembic/versions/w8r9s0t1u2v3_fix_mental_models_pk_isolation.py new file mode 100644 index 00000000..c384f78b --- /dev/null +++ b/hindsight-api/hindsight_api/alembic/versions/w8r9s0t1u2v3_fix_mental_models_pk_isolation.py @@ -0,0 +1,60 @@ +"""Fix mental_models primary key to be scoped per bank + +Revision ID: w8r9s0t1u2v3 +Revises: v7q8r9s0t1u2 +Create Date: 2026-02-05 + +This migration fixes a critical bank isolation bug where mental_models.id was +globally unique across all banks instead of being scoped per bank. This caused +conflicts when different banks tried to use the same custom ID. + +CRITICAL FIX: Changes primary key from (id) to (bank_id, id) to ensure proper isolation. +""" + +from collections.abc import Sequence + +from alembic import context, op + +revision: str = "w8r9s0t1u2v3" +down_revision: str | Sequence[str] | None = "v7q8r9s0t1u2" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def _get_schema_prefix() -> str: + """Get schema prefix for table names (required for multi-tenant support).""" + schema = context.config.get_main_option("target_schema") + return f'"{schema}".' if schema else "" + + +def upgrade() -> None: + """Change mental_models primary key from (id) to (bank_id, id) for proper bank isolation.""" + schema = _get_schema_prefix() + + # Drop the old primary key constraint (just id) + # Note: The constraint might be named differently on different DBs + # Try both old names (pinned_reflections_pkey from original, mental_models_pkey from rename) + op.execute(f"ALTER TABLE {schema}mental_models DROP CONSTRAINT IF EXISTS pinned_reflections_pkey") + op.execute(f"ALTER TABLE {schema}mental_models DROP CONSTRAINT IF EXISTS mental_models_pkey") + + # Create the new composite primary key (bank_id, id) + # This ensures IDs are scoped per bank, not globally + op.execute(f""" + ALTER TABLE {schema}mental_models + ADD CONSTRAINT mental_models_pkey PRIMARY KEY (bank_id, id) + """) + + +def downgrade() -> None: + """Revert mental_models primary key from (bank_id, id) to (id).""" + schema = _get_schema_prefix() + + # Drop the composite primary key + op.execute(f"ALTER TABLE {schema}mental_models DROP CONSTRAINT IF EXISTS mental_models_pkey") + + # Restore the old primary key (just id) + # WARNING: This downgrade will fail if there are duplicate IDs across banks + op.execute(f""" + ALTER TABLE {schema}mental_models + ADD CONSTRAINT mental_models_pkey PRIMARY KEY (id) + """) diff --git a/hindsight-api/hindsight_api/api/http.py b/hindsight-api/hindsight_api/api/http.py index df463a6b..1cc4e170 100644 --- a/hindsight-api/hindsight_api/api/http.py +++ b/hindsight-api/hindsight_api/api/http.py @@ -523,7 +523,9 @@ class ReflectFact(BaseModel): ) id: str | None = None - text: str + text: str = Field( + description="Fact text. When type='observation', this contains markdown-formatted consolidated knowledge" + ) type: str | None = None # fact type: world, experience, observation context: str | None = None occurred_start: str | None = None @@ -588,7 +590,7 @@ class ReflectResponse(BaseModel): model_config = ConfigDict( json_schema_extra={ "example": { - "text": "Based on my understanding, AI is a transformative technology...", + "text": "## AI Overview\n\nBased on my understanding, AI is a **transformative technology**:\n\n- Used extensively in healthcare\n- Discussed in recent conversations\n- Continues to evolve rapidly", "based_on": { "memories": [ {"id": "123", "text": "AI is used in healthcare", "type": "world"}, @@ -616,7 +618,9 @@ class ReflectResponse(BaseModel): } ) - text: str + text: str = Field( + description="The reflect response as well-formatted markdown (headers, lists, bold/italic, code blocks, etc.)" + ) based_on: ReflectBasedOn | None = Field( default=None, description="Evidence used to generate the response. Only present when include.facts is set.", @@ -1114,7 +1118,9 @@ class MentalModelResponse(BaseModel): bank_id: str name: str source_query: str - content: str + content: str = Field( + description="The mental model content as well-formatted markdown (auto-generated from reflect endpoint)" + ) tags: list[str] = Field(default_factory=list) max_tokens: int = Field(default=2048) trigger: MentalModelTrigger = Field(default_factory=MentalModelTrigger) diff --git a/hindsight-api/hindsight_api/engine/consolidation/prompts.py b/hindsight-api/hindsight_api/engine/consolidation/prompts.py index 441e19f4..31cc316a 100644 --- a/hindsight-api/hindsight_api/engine/consolidation/prompts.py +++ b/hindsight-api/hindsight_api/engine/consolidation/prompts.py @@ -2,7 +2,7 @@ CONSOLIDATION_SYSTEM_PROMPT = """You are a memory consolidation system. Your job is to convert facts into durable knowledge (observations) and merge with existing knowledge when appropriate. -You must output ONLY valid JSON with no markdown formatting, no code blocks, and no additional text. +You must output ONLY valid JSON with no markdown code blocks or additional text. However, the "text" field within each observation should use markdown formatting (headers, lists, bold, etc.) for clarity and readability. ## EXTRACT DURABLE KNOWLEDGE, NOT EPHEMERAL STATE Facts often describe events or actions. Extract the DURABLE KNOWLEDGE implied by the fact, not the transient state. @@ -71,10 +71,15 @@ Instructions: - New topic → CREATE new observation - Purely ephemeral → return [] -Output JSON array of actions: +Output JSON array of actions (the "text" field should use markdown formatting for structure): [ - {{"action": "update", "learning_id": "uuid-from-observations", "text": "updated knowledge", "reason": "..."}}, - {{"action": "create", "text": "new durable knowledge", "reason": "..."}} + {{"action": "update", "learning_id": "uuid-from-observations", "text": "## Updated Knowledge\n\n**Key point**: details here\n\n- Supporting detail 1\n- Supporting detail 2", "reason": "..."}}, + {{"action": "create", "text": "## New Durable Knowledge\n\nDescription with **emphasis** and proper structure", "reason": "..."}} ] -Return [] if fact contains no durable knowledge.""" +Return [] if fact contains no durable knowledge. + +IMPORTANT: Format the "text" field with markdown for better readability: +- Use headers, lists, bold/italic, tables where appropriate +- CRITICAL: Add blank lines before and after block elements (tables, code blocks, lists) +- Ensure proper spacing for markdown to render correctly""" diff --git a/hindsight-api/hindsight_api/engine/reflect/models.py b/hindsight-api/hindsight_api/engine/reflect/models.py index 500ec34a..26c3f150 100644 --- a/hindsight-api/hindsight_api/engine/reflect/models.py +++ b/hindsight-api/hindsight_api/engine/reflect/models.py @@ -31,7 +31,7 @@ class ReflectAction(BaseModel): default=None, description="Observation sections for done action (when output_mode=observations)" ) # Plain text answer fields (for output_mode=answer) - answer: str | None = Field(default=None, description="Plain text answer for done action (no markdown)") + answer: str | None = Field(default=None, description="Well-formatted markdown answer for done action") answer_memory_ids: list[str] | None = Field( default=None, description="Memory IDs supporting the answer", alias="memory_ids" ) diff --git a/hindsight-api/hindsight_api/engine/reflect/prompts.py b/hindsight-api/hindsight_api/engine/reflect/prompts.py index 7a0296b3..72b09d39 100644 --- a/hindsight-api/hindsight_api/engine/reflect/prompts.py +++ b/hindsight-api/hindsight_api/engine/reflect/prompts.py @@ -300,9 +300,11 @@ def build_system_prompt_for_tools( parts.extend( [ "", - "## Output Format: Plain Text Answer", - "Call done() with a plain text 'answer' field.", - "- Do NOT use markdown formatting", + "## Output Format: Well-Formatted Markdown Answer", + "Call done() with a well-formatted markdown 'answer' field.", + "- USE markdown formatting for structure (headers, lists, bold, italic, code blocks, tables, etc.)", + "- CRITICAL: Add blank lines before and after block elements (tables, code blocks, lists)", + "- Format for clarity and readability with proper spacing and hierarchy", "- NEVER include memory IDs, UUIDs, or 'Memory references' in the answer text", "- Put IDs ONLY in the memory_ids/mental_model_ids/observation_ids arrays, not in the answer", ] @@ -485,8 +487,17 @@ Your approach: Only say "I don't have information" if the retrieved data is truly unrelated to the question. Do NOT fabricate information that has no basis in the retrieved data. +FORMATTING: Use proper markdown formatting in your answer: +- Headers (##, ###) for sections +- Lists (bullet or numbered) for enumerations +- Bold/italic for emphasis +- Tables with proper syntax (ensure blank line before and after) +- Code blocks where appropriate +- CRITICAL: Always add blank lines before and after block elements (tables, code blocks, lists) +- Proper spacing between sections + CRITICAL: Output ONLY the final synthesized answer. Do NOT include: - Meta-commentary about what you're doing ("I'll search...", "Let me analyze...") - Explanations of your reasoning process - Descriptions of your approach -Just provide the direct answer.""" +Just provide the direct answer with proper markdown formatting.""" diff --git a/hindsight-api/hindsight_api/engine/reflect/tools_schema.py b/hindsight-api/hindsight_api/engine/reflect/tools_schema.py index a4d9ebe4..8d342506 100644 --- a/hindsight-api/hindsight_api/engine/reflect/tools_schema.py +++ b/hindsight-api/hindsight_api/engine/reflect/tools_schema.py @@ -139,7 +139,7 @@ TOOL_DONE_ANSWER = { "properties": { "answer": { "type": "string", - "description": "Your response as plain text. Do NOT use markdown formatting. NEVER include memory IDs, UUIDs, or 'Memory references' in this text - put IDs only in memory_ids array.", + "description": "Your response as well-formatted markdown. Use headers, lists, bold/italic, and code blocks for clarity. NEVER include memory IDs, UUIDs, or 'Memory references' in this text - put IDs only in memory_ids array.", }, "memory_ids": { "type": "array", @@ -190,7 +190,7 @@ def _build_done_tool_with_directives(directive_rules: list[str]) -> dict: "properties": { "answer": { "type": "string", - "description": "Your response as plain text. Do NOT use markdown formatting. NEVER include memory IDs, UUIDs, or 'Memory references' in this text - put IDs only in memory_ids array.", + "description": "Your response as well-formatted markdown. Use headers, lists, bold/italic, and code blocks for clarity. NEVER include memory IDs, UUIDs, or 'Memory references' in this text - put IDs only in memory_ids array.", }, "memory_ids": { "type": "array", diff --git a/hindsight-clients/python/hindsight_client_api/models/mental_model_response.py b/hindsight-clients/python/hindsight_client_api/models/mental_model_response.py index 68b36ea6..37c42b47 100644 --- a/hindsight-clients/python/hindsight_client_api/models/mental_model_response.py +++ b/hindsight-clients/python/hindsight_client_api/models/mental_model_response.py @@ -17,7 +17,7 @@ import pprint import re # noqa: F401 import json -from pydantic import BaseModel, ConfigDict, StrictInt, StrictStr +from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr from typing import Any, ClassVar, Dict, List, Optional from hindsight_client_api.models.mental_model_trigger import MentalModelTrigger from typing import Optional, Set @@ -31,7 +31,7 @@ class MentalModelResponse(BaseModel): bank_id: StrictStr name: StrictStr source_query: StrictStr - content: StrictStr + content: StrictStr = Field(description="The mental model content as well-formatted markdown (auto-generated from reflect endpoint)") tags: Optional[List[StrictStr]] = None max_tokens: Optional[StrictInt] = 2048 trigger: Optional[MentalModelTrigger] = None diff --git a/hindsight-clients/python/hindsight_client_api/models/reflect_fact.py b/hindsight-clients/python/hindsight_client_api/models/reflect_fact.py index 3f22e347..2e06de76 100644 --- a/hindsight-clients/python/hindsight_client_api/models/reflect_fact.py +++ b/hindsight-clients/python/hindsight_client_api/models/reflect_fact.py @@ -17,7 +17,7 @@ import pprint import re # noqa: F401 import json -from pydantic import BaseModel, ConfigDict, StrictStr +from pydantic import BaseModel, ConfigDict, Field, StrictStr from typing import Any, ClassVar, Dict, List, Optional from typing import Optional, Set from typing_extensions import Self @@ -27,7 +27,7 @@ class ReflectFact(BaseModel): A fact used in think response. """ # noqa: E501 id: Optional[StrictStr] = None - text: StrictStr + text: StrictStr = Field(description="Fact text. When type='observation', this contains markdown-formatted consolidated knowledge") type: Optional[StrictStr] = None context: Optional[StrictStr] = None occurred_start: Optional[StrictStr] = None diff --git a/hindsight-clients/python/hindsight_client_api/models/reflect_response.py b/hindsight-clients/python/hindsight_client_api/models/reflect_response.py index c1d08b88..a319dd64 100644 --- a/hindsight-clients/python/hindsight_client_api/models/reflect_response.py +++ b/hindsight-clients/python/hindsight_client_api/models/reflect_response.py @@ -17,7 +17,7 @@ import pprint import re # noqa: F401 import json -from pydantic import BaseModel, ConfigDict, StrictStr +from pydantic import BaseModel, ConfigDict, Field, StrictStr from typing import Any, ClassVar, Dict, List, Optional from hindsight_client_api.models.reflect_based_on import ReflectBasedOn from hindsight_client_api.models.reflect_trace import ReflectTrace @@ -29,7 +29,7 @@ class ReflectResponse(BaseModel): """ Response model for think endpoint. """ # noqa: E501 - text: StrictStr + text: StrictStr = Field(description="The reflect response as well-formatted markdown (headers, lists, bold/italic, code blocks, etc.)") based_on: Optional[ReflectBasedOn] = None structured_output: Optional[Dict[str, Any]] = None usage: Optional[TokenUsage] = None diff --git a/hindsight-clients/typescript/generated/types.gen.ts b/hindsight-clients/typescript/generated/types.gen.ts index ab9c1cc9..b8b03ba2 100644 --- a/hindsight-clients/typescript/generated/types.gen.ts +++ b/hindsight-clients/typescript/generated/types.gen.ts @@ -1034,6 +1034,8 @@ export type MentalModelResponse = { source_query: string; /** * Content + * + * The mental model content as well-formatted markdown (auto-generated from reflect endpoint) */ content: string; /** @@ -1382,6 +1384,8 @@ export type ReflectFact = { id?: string | null; /** * Text + * + * Fact text. When type='observation', this contains markdown-formatted consolidated knowledge */ text: string; /** @@ -1523,6 +1527,8 @@ export type ReflectRequest = { export type ReflectResponse = { /** * Text + * + * The reflect response as well-formatted markdown (headers, lists, bold/italic, code blocks, etc.) */ text: string; /** diff --git a/hindsight-control-plane/package.json b/hindsight-control-plane/package.json index 585ab4c3..2434041a 100644 --- a/hindsight-control-plane/package.json +++ b/hindsight-control-plane/package.json @@ -63,6 +63,7 @@ "react-markdown": "^10.1.0", "react18-json-view": "^0.2.9", "recharts": "^3.5.1", + "remark-gfm": "^4.0.1", "tailwind-merge": "^3.4.0", "tailwindcss": "^4.1.17", "tailwindcss-animate": "^1.0.7", diff --git a/hindsight-control-plane/src/app/globals.css b/hindsight-control-plane/src/app/globals.css index b58199f1..cfa5049b 100644 --- a/hindsight-control-plane/src/app/globals.css +++ b/hindsight-control-plane/src/app/globals.css @@ -189,4 +189,75 @@ input[type="datetime-local"]::-webkit-calendar-picker-indicator { .dark input[type="datetime-local"]::-webkit-calendar-picker-indicator { filter: invert(1); +} + +/* Markdown table styles - explicitly override Tailwind reset */ +.prose table { + width: 100%; + border-collapse: collapse; + margin-top: 1em; + margin-bottom: 1em; + font-size: 0.875em; + line-height: 1.5; + border: 2px solid rgba(0, 0, 0, 0.2) !important; +} + +.prose thead { + border-bottom: 3px solid rgba(0, 0, 0, 0.3) !important; + background-color: rgba(0, 0, 0, 0.05); +} + +.prose thead th { + padding: 0.5rem 0.75rem; + text-align: left; + font-weight: 600; + vertical-align: bottom; + border: 1px solid rgba(0, 0, 0, 0.2) !important; + border-bottom-width: 3px !important; +} + +.prose tbody tr { + border-bottom: 1px solid rgba(0, 0, 0, 0.15) !important; +} + +.prose tbody tr:last-child { + border-bottom: 1px solid rgba(0, 0, 0, 0.15) !important; +} + +.prose tbody td { + padding: 0.5rem 0.75rem; + vertical-align: top; + border: 1px solid rgba(0, 0, 0, 0.15) !important; +} + +.prose tbody tr:hover { + background-color: rgba(0, 0, 0, 0.04); +} + +/* Dark mode table styles - use white borders with transparency */ +.dark .prose table { + color: hsl(var(--foreground)); + border: 2px solid rgba(255, 255, 255, 0.2) !important; +} + +.dark .prose thead { + border-bottom: 3px solid rgba(255, 255, 255, 0.3) !important; + background-color: rgba(255, 255, 255, 0.05); +} + +.dark .prose thead th { + border: 1px solid rgba(255, 255, 255, 0.2) !important; + border-bottom-width: 3px !important; +} + +.dark .prose tbody tr { + border-bottom: 1px solid rgba(255, 255, 255, 0.15) !important; +} + +.dark .prose tbody td { + border: 1px solid rgba(255, 255, 255, 0.15) !important; +} + +.dark .prose tbody tr:hover { + background-color: rgba(255, 255, 255, 0.05); } \ No newline at end of file diff --git a/hindsight-control-plane/src/components/bank-profile-view.tsx b/hindsight-control-plane/src/components/bank-profile-view.tsx index ef5a8bf5..b0913f90 100644 --- a/hindsight-control-plane/src/components/bank-profile-view.tsx +++ b/hindsight-control-plane/src/components/bank-profile-view.tsx @@ -2,6 +2,7 @@ import { useState, useEffect, useRef } from "react"; import ReactMarkdown from "react-markdown"; +import remarkGfm from "remark-gfm"; import { useRouter } from "next/navigation"; import { client } from "@/lib/api"; import { useBank } from "@/lib/bank-context"; @@ -1343,7 +1344,7 @@ function DirectiveDetailPanel({ Rule
- {directive.content} + {directive.content}
diff --git a/hindsight-control-plane/src/components/mental-model-detail-modal.tsx b/hindsight-control-plane/src/components/mental-model-detail-modal.tsx index 838232d1..45b3ae96 100644 --- a/hindsight-control-plane/src/components/mental-model-detail-modal.tsx +++ b/hindsight-control-plane/src/components/mental-model-detail-modal.tsx @@ -7,6 +7,7 @@ import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"; import { VisuallyHidden } from "@radix-ui/react-visually-hidden"; import { Loader2, Zap } from "lucide-react"; import ReactMarkdown from "react-markdown"; +import remarkGfm from "remark-gfm"; interface MentalModelDetailContentProps { mentalModel: MentalModel; @@ -73,7 +74,7 @@ export function MentalModelDetailContent({ mentalModel }: MentalModelDetailConte Content
- {mentalModel.content} + {mentalModel.content}
diff --git a/hindsight-control-plane/src/components/mental-models-view.tsx b/hindsight-control-plane/src/components/mental-models-view.tsx index b2d1d77f..b29cee53 100644 --- a/hindsight-control-plane/src/components/mental-models-view.tsx +++ b/hindsight-control-plane/src/components/mental-models-view.tsx @@ -2,6 +2,7 @@ import { useState, useEffect } from "react"; import ReactMarkdown from "react-markdown"; +import remarkGfm from "remark-gfm"; import { client } from "@/lib/api"; import { useBank } from "@/lib/bank-context"; import { Button } from "@/components/ui/button"; @@ -302,8 +303,24 @@ export function MentalModelsView() {

{m.source_query}

-
- {m.content} +
+ {/* Check if content has tables or complex markdown */} + {m.content.includes("|") || + m.content.includes("```") || + m.content.includes("\n\n") ? ( + // Show plain text preview for complex content +
+ {m.content.substring(0, 150)}...{" "} + Click to view full content +
+ ) : ( + // Render simple markdown with line clamp +
+ + {m.content} + +
+ )}
@@ -1115,7 +1132,7 @@ function MentalModelDetailPanel({ Content
- {mentalModel.content} + {mentalModel.content}
diff --git a/hindsight-control-plane/src/components/think-view.tsx b/hindsight-control-plane/src/components/think-view.tsx index 149a0878..4fedb067 100644 --- a/hindsight-control-plane/src/components/think-view.tsx +++ b/hindsight-control-plane/src/components/think-view.tsx @@ -32,6 +32,8 @@ import JsonView from "react18-json-view"; import "react18-json-view/src/style.css"; import { MemoryDetailModal } from "./memory-detail-modal"; import { MentalModelDetailModal } from "./mental-model-detail-modal"; +import ReactMarkdown from "react-markdown"; +import remarkGfm from "remark-gfm"; type TagsMatch = "any" | "all" | "any_strict" | "all_strict"; type ViewMode = "answer" | "trace" | "json"; @@ -364,7 +366,9 @@ export function ThinkView() { Answer -
{result.text}
+
+ {result.text} +
@@ -967,9 +971,11 @@ export function ThinkView() {

Text

-

- {fullObservation?.text || selectedObservation.text} -

+
+ + {fullObservation?.text || selectedObservation.text} + +
{fullObservation?.tags && fullObservation.tags.length > 0 && (
diff --git a/hindsight-docs/static/openapi.json b/hindsight-docs/static/openapi.json index e9c9fad2..a674fa96 100644 --- a/hindsight-docs/static/openapi.json +++ b/hindsight-docs/static/openapi.json @@ -4678,7 +4678,8 @@ }, "content": { "type": "string", - "title": "Content" + "title": "Content", + "description": "The mental model content as well-formatted markdown (auto-generated from reflect endpoint)" }, "tags": { "items": { @@ -5397,7 +5398,8 @@ }, "text": { "type": "string", - "title": "Text" + "title": "Text", + "description": "Fact text. When type='observation', this contains markdown-formatted consolidated knowledge" }, "type": { "anyOf": [ @@ -5657,7 +5659,8 @@ "properties": { "text": { "type": "string", - "title": "Text" + "title": "Text", + "description": "The reflect response as well-formatted markdown (headers, lists, bold/italic, code blocks, etc.)" }, "based_on": { "anyOf": [ @@ -5734,7 +5737,7 @@ ], "summary": "AI is transformative" }, - "text": "Based on my understanding, AI is a transformative technology...", + "text": "## AI Overview\n\nBased on my understanding, AI is a **transformative technology**:\n\n- Used extensively in healthcare\n- Discussed in recent conversations\n- Continues to evolve rapidly", "trace": { "llm_calls": [ { diff --git a/package-lock.json b/package-lock.json index 66bc0d44..e0cdcdca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ }, "hindsight-clients/typescript": { "name": "@vectorize-io/hindsight-client", - "version": "0.4.4", + "version": "0.4.9", "license": "MIT", "devDependencies": { "@hey-api/openapi-ts": "0.88.0", @@ -131,7 +131,7 @@ }, "hindsight-control-plane": { "name": "@vectorize-io/hindsight-control-plane", - "version": "0.4.4", + "version": "0.4.9", "license": "ISC", "dependencies": { "@radix-ui/react-alert-dialog": "^1.1.15", @@ -170,6 +170,7 @@ "react-markdown": "^10.1.0", "react18-json-view": "^0.2.9", "recharts": "^3.5.1", + "remark-gfm": "^4.0.1", "tailwind-merge": "^3.4.0", "tailwindcss": "^4.1.17", "tailwindcss-animate": "^1.0.7",