diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c13aa109..e14e55050 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Exa tools no longer MCP. +- Adapt everything to vercel v5. +- Adapt to Response API. + ## [v0.11.5] - 6.11.2025 @@ -104,6 +107,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Try to enforce using metric tools rather than downloading assets. - Rule to avoid overvalidating. + ## [v0.10.0] - 2.10.2025 ### Fixed diff --git a/backend/alembic/versions/25cefa8449c6_change_to_response_api.py b/backend/alembic/versions/25cefa8449c6_change_to_response_api.py new file mode 100644 index 000000000..87cef7578 --- /dev/null +++ b/backend/alembic/versions/25cefa8449c6_change_to_response_api.py @@ -0,0 +1,935 @@ +"""change_to_response_api + +Revision ID: 25cefa8449c6 +Revises: 6d8986f38d7b +Create Date: 2025-11-25 16:10:42.083480 + +""" + +import json +from typing import Sequence, Union + +import sqlalchemy as sa +from sqlalchemy.dialects.postgresql import JSONB + +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "25cefa8449c6" +down_revision: Union[str, None] = "6d8986f38d7b" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # Remove search vector index + op.drop_index("ix_messages_search_vector", table_name="messages", if_exists=True) + + # Create Parts table + op.create_table( + "parts", + sa.Column("part_id", sa.UUID(), nullable=False), + sa.Column("message_id", sa.UUID(), nullable=False), + sa.Column("order_index", sa.Integer(), nullable=False), + sa.Column( + "type", + sa.Enum( + "MESSAGE", + "REASONING", + "FUNCTION_CALL", + "FUNCTION_CALL_OUTPUT", + name="parttype", + create_type=False, + ), + nullable=False, + ), + sa.Column("output", JSONB, nullable=False), + sa.Column("is_complete", sa.Boolean(), nullable=False), + sa.Column("validated", sa.Boolean(), nullable=True), + sa.PrimaryKeyConstraint("part_id"), + sa.ForeignKeyConstraint(["message_id"], ["messages.message_id"]), + ) + op.create_index("ix_parts_message_id", "parts", ["message_id"]) + + # Migrate data + conn = op.get_bind() + + # Get all threads + threads = conn.execute( + sa.text("SELECT thread_id FROM threads ORDER BY creation_date") + ).fetchall() + + for (thread_id,) in threads: + # Get all messages in this thread ordered by creation_date + messages = conn.execute( + sa.text(""" + SELECT message_id, entity, content, creation_date, is_complete + FROM messages + WHERE thread_id = :thread_id + ORDER BY creation_date + """), + {"thread_id": thread_id}, + ).fetchall() + + i = 0 + while i < len(messages): + msg_id, entity, content, creation_date, is_complete = messages[i] + + # Parse content as JSON + try: + content_json = json.loads(content) + except: + content_json = {"content": content} + + if entity == "USER": + # Create USER message with one MESSAGE part + user_text = content_json.get("content", "") + conn.execute( + sa.text(""" + INSERT INTO parts (part_id, message_id, order_index, type, output, is_complete, validated) + VALUES (gen_random_uuid(), :message_id, 0, 'MESSAGE', :output, :is_complete, NULL) + """), + { + "message_id": msg_id, + "output": json.dumps( + { + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": user_text}], + "status": "completed", + } + ), + "is_complete": is_complete, + }, + ) + i += 1 + + elif entity in ("AI_TOOL", "AI_MESSAGE"): + # Aggregate all AI responses into ONE ASSISTANT message until next USER + assistant_msg_id = msg_id + order_idx = 0 + messages_to_delete = [] + + # Loop through all AI messages until we hit a USER message + while i < len(messages) and messages[i][1] in ( + "AI_TOOL", + "AI_MESSAGE", + "TOOL", + ): + ( + curr_msg_id, + curr_entity, + curr_content, + curr_creation_date, + curr_is_complete, + ) = messages[i] + + try: + curr_content_json = json.loads(curr_content) + except: + curr_content_json = {"content": curr_content} + + if curr_entity == "AI_TOOL": + # Add reasoning if present + reasoning = curr_content_json.get("reasoning", []) + encrypted_reasoning = curr_content_json.get( + "encrypted_reasoning", "" + ) + # Convert string to list if needed (handle JSON-encoded strings) + if isinstance(reasoning, str): + try: + reasoning = json.loads(reasoning) + except: + reasoning = [reasoning] if reasoning else [] + if reasoning: + summary = [ + {"type": "summary_text", "text": step} + for step in reasoning + ] + conn.execute( + sa.text(""" + INSERT INTO parts (part_id, message_id, order_index, type, output, is_complete, validated) + VALUES (gen_random_uuid(), :message_id, :order_index, 'REASONING', :output, :is_complete, NULL) + """), + { + "message_id": assistant_msg_id, + "order_index": order_idx, + "output": json.dumps( + { + "type": "reasoning", + "encrypted_content": encrypted_reasoning, + "summary": summary, + } + ), + "is_complete": curr_is_complete, + }, + ) + order_idx += 1 + + # Add content as MESSAGE if present + msg_content = curr_content_json.get("content", "") + if msg_content: + conn.execute( + sa.text(""" + INSERT INTO parts (part_id, message_id, order_index, type, output, is_complete, validated) + VALUES (gen_random_uuid(), :message_id, :order_index, 'MESSAGE', :output, :is_complete, NULL) + """), + { + "message_id": assistant_msg_id, + "order_index": order_idx, + "output": json.dumps( + { + "type": "message", + "role": "assistant", + "content": [ + { + "type": "output_text", + "text": msg_content, + } + ], + "status": "completed", + } + ), + "is_complete": curr_is_complete, + }, + ) + order_idx += 1 + + # Get tool calls with validated + tool_calls = conn.execute( + sa.text(""" + SELECT tool_call_id, name, arguments, validated + FROM tool_calls + WHERE message_id = :message_id + ORDER BY tool_call_id + """), + {"message_id": curr_msg_id}, + ).fetchall() + + # Add FUNCTION_CALL parts + for tool_call_id, name, arguments, validated in tool_calls: + conn.execute( + sa.text(""" + INSERT INTO parts (part_id, message_id, order_index, type, output, is_complete, validated) + VALUES (gen_random_uuid(), :message_id, :order_index, 'FUNCTION_CALL', :output, :is_complete, :validated) + """), + { + "message_id": assistant_msg_id, + "order_index": order_idx, + "output": json.dumps( + { + "type": "function_call", + "call_id": tool_call_id, + "name": name, + "arguments": arguments, + "status": "completed", + } + ), + "is_complete": curr_is_complete, + "validated": validated, + }, + ) + order_idx += 1 + + if curr_msg_id != assistant_msg_id: + messages_to_delete.append(curr_msg_id) + i += 1 + + elif curr_entity == "TOOL": + # Add FUNCTION_CALL_OUTPUT part + conn.execute( + sa.text(""" + INSERT INTO parts (part_id, message_id, order_index, type, output, is_complete, validated) + VALUES (gen_random_uuid(), :message_id, :order_index, 'FUNCTION_CALL_OUTPUT', :output, :is_complete, NULL) + """), + { + "message_id": assistant_msg_id, + "order_index": order_idx, + "output": json.dumps( + { + "type": "function_call_output", + "call_id": curr_content_json.get( + "tool_call_id", "" + ), + "output": curr_content_json.get("content", ""), + "status": "completed", + } + ), + "is_complete": curr_is_complete, + }, + ) + order_idx += 1 + messages_to_delete.append(curr_msg_id) + i += 1 + + elif curr_entity == "AI_MESSAGE": + # Add reasoning if present + reasoning = curr_content_json.get("reasoning", []) + encrypted_reasoning = curr_content_json.get( + "encrypted_reasoning", "" + ) + # Convert string to list if needed (handle JSON-encoded strings) + if isinstance(reasoning, str): + try: + reasoning = json.loads(reasoning) + except: + reasoning = [reasoning] if reasoning else [] + if reasoning: + summary = [ + {"type": "summary_text", "text": step} + for step in reasoning + ] + conn.execute( + sa.text(""" + INSERT INTO parts (part_id, message_id, order_index, type, output, is_complete, validated) + VALUES (gen_random_uuid(), :message_id, :order_index, 'REASONING', :output, :is_complete, NULL) + """), + { + "message_id": assistant_msg_id, + "order_index": order_idx, + "output": json.dumps( + { + "type": "reasoning", + "encrypted_content": encrypted_reasoning, + "summary": summary, + } + ), + "is_complete": curr_is_complete, + }, + ) + order_idx += 1 + + # Add final MESSAGE part + msg_content = curr_content_json.get("content", "") + conn.execute( + sa.text(""" + INSERT INTO parts (part_id, message_id, order_index, type, output, is_complete, validated) + VALUES (gen_random_uuid(), :message_id, :order_index, 'MESSAGE', :output, :is_complete, NULL) + """), + { + "message_id": assistant_msg_id, + "order_index": order_idx, + "output": json.dumps( + { + "type": "message", + "role": "assistant", + "content": [ + {"type": "output_text", "text": msg_content} + ], + "status": "completed", + } + ), + "is_complete": curr_is_complete, + }, + ) + if curr_msg_id != assistant_msg_id: + messages_to_delete.append(curr_msg_id) + i += 1 + + # Move foreign keys and delete old messages + for old_msg_id in messages_to_delete: + conn.execute( + sa.text( + "UPDATE tool_calls SET message_id = :new_id WHERE message_id = :old_id" + ), + {"new_id": assistant_msg_id, "old_id": old_msg_id}, + ) + conn.execute( + sa.text( + "UPDATE token_consumption SET message_id = :new_id WHERE message_id = :old_id" + ), + {"new_id": assistant_msg_id, "old_id": old_msg_id}, + ) + conn.execute( + sa.text( + "UPDATE tool_selection SET message_id = :new_id WHERE message_id = :old_id" + ), + {"new_id": assistant_msg_id, "old_id": old_msg_id}, + ) + conn.execute( + sa.text( + "UPDATE complexity_estimation SET message_id = :new_id WHERE message_id = :old_id" + ), + {"new_id": assistant_msg_id, "old_id": old_msg_id}, + ) + conn.execute( + sa.text("DELETE FROM messages WHERE message_id = :message_id"), + {"message_id": old_msg_id}, + ) + elif entity == "TOOL": + # TOOL messages are handled within AI_TOOL blocks, skip standalone ones + i += 1 + else: + # Skip unknown entity types + i += 1 + + # drop old trigger/function if present + op.execute("DROP TRIGGER IF EXISTS messages_search_vector_trigger ON messages;") + op.execute("DROP TRIGGER IF EXISTS parts_search_vector_trigger ON parts;") + op.execute("DROP FUNCTION IF EXISTS update_messages_search_vector();") + + # create new function that updates parent message search vector from parts + op.execute(""" + CREATE OR REPLACE FUNCTION update_messages_search_vector() + RETURNS TRIGGER AS $$ + BEGIN + UPDATE messages + SET search_vector = to_tsvector('english', COALESCE( + (SELECT p.output->'content'->0->>'text' + FROM parts p + WHERE p.message_id = COALESCE(NEW.message_id, OLD.message_id) AND p.type = 'MESSAGE' + ORDER BY p.order_index DESC + LIMIT 1 + ), '' + )) + WHERE message_id = COALESCE(NEW.message_id, OLD.message_id); + RETURN NEW; + END; + $$ LANGUAGE plpgsql; + """) + + # create trigger on parts table + op.execute(""" + CREATE TRIGGER parts_search_vector_trigger + AFTER INSERT OR UPDATE OR DELETE ON parts + FOR EACH ROW + EXECUTE FUNCTION update_messages_search_vector(); + """) + + # populate search_vector for existing rows + op.execute(""" + UPDATE messages + SET search_vector = to_tsvector('english', COALESCE( + (SELECT p.output->'content'->0->>'text' + FROM parts p + WHERE p.message_id = messages.message_id AND p.type = 'MESSAGE' + ORDER BY p.order_index DESC + LIMIT 1 + ), '' + )); + """) + + # Convert entity column to text temporarily + op.execute("ALTER TABLE messages ALTER COLUMN entity TYPE text") + + # Delete TOOL messages (already converted to parts) + conn.execute(sa.text("DELETE FROM messages WHERE entity = 'TOOL'")) + + # Update all AI_TOOL and AI_MESSAGE to ASSISTANT + conn.execute( + sa.text( + "UPDATE messages SET entity = 'ASSISTANT' WHERE entity IN ('AI_TOOL', 'AI_MESSAGE')" + ) + ) + + # Drop old enum and create new one + op.execute("DROP TYPE entity") + op.execute("CREATE TYPE entity AS ENUM ('USER', 'ASSISTANT')") + + # Convert column back to enum + op.execute( + "ALTER TABLE messages ALTER COLUMN entity TYPE entity USING entity::entity" + ) + + # Drop old columns and tables + op.drop_table("tool_calls") + op.drop_column("messages", "content") + op.drop_column("messages", "is_complete") + + +def downgrade(): + conn = op.get_bind() + + # Add back content and is_complete columns + op.add_column("messages", sa.Column("content", sa.String(), nullable=True)) + op.add_column("messages", sa.Column("is_complete", sa.Boolean(), nullable=True)) + + # Recreate tool_calls table + op.create_table( + "tool_calls", + sa.Column("tool_call_id", sa.String(), nullable=False), + sa.Column("message_id", sa.UUID(), nullable=False), + sa.Column("name", sa.String(), nullable=False), + sa.Column("arguments", sa.String(), nullable=False), + sa.Column("validated", sa.Boolean(), nullable=True), + sa.PrimaryKeyConstraint("tool_call_id"), + sa.ForeignKeyConstraint(["message_id"], ["messages.message_id"]), + ) + + # Convert entity enum back to old format + op.execute("ALTER TABLE messages ALTER COLUMN entity TYPE text") + + # Migrate data back from Parts to old format (must happen before enum conversion) + threads = conn.execute( + sa.text("SELECT thread_id FROM threads ORDER BY creation_date") + ).fetchall() + + for (thread_id,) in threads: + messages = conn.execute( + sa.text(""" + SELECT message_id, entity, creation_date + FROM messages + WHERE thread_id = :thread_id + ORDER BY creation_date + """), + {"thread_id": thread_id}, + ).fetchall() + + for msg_id, entity, creation_date in messages: + if entity == "USER": + # Get USER message part + part = conn.execute( + sa.text(""" + SELECT output, is_complete FROM parts + WHERE message_id = :message_id AND type = 'MESSAGE' + ORDER BY order_index LIMIT 1 + """), + {"message_id": msg_id}, + ).fetchone() + if part: + output = ( + part[0] if isinstance(part[0], dict) else json.loads(part[0]) + ) + text = output.get("content", [{}])[0].get("text", "") + conn.execute( + sa.text( + "UPDATE messages SET content = :content, is_complete = :is_complete WHERE message_id = :message_id" + ), + { + "content": json.dumps({"role": "user", "content": text}), + "is_complete": part[1], + "message_id": msg_id, + }, + ) + + elif entity == "ASSISTANT": + # Group parts by turn (turn boundary = after all FUNCTION_CALL_OUTPUT) + turns = [] + current_turn = { + "reasoning": "", + "content": "", + "tool_calls": [], + "tool_outputs": [], + "is_complete": True, + } + + # Get all parts with is_complete and validated + parts_with_complete = conn.execute( + sa.text(""" + SELECT type, output, is_complete, validated + FROM parts + WHERE message_id = :message_id + ORDER BY order_index + """), + {"message_id": msg_id}, + ).fetchall() + + for idx, (part_type, output, is_complete_part, validated) in enumerate( + parts_with_complete + ): + output_json = ( + output if isinstance(output, dict) else json.loads(output) + ) + + current_turn["is_complete"] = is_complete_part + + if part_type == "REASONING": + summary = output_json.get("summary", []) + # Convert list to JSON string for downgrade (preserves structure) + reasoning_list = [s.get("text", "") for s in summary] + current_turn["reasoning"] = ( + json.dumps(reasoning_list) if reasoning_list else "" + ) + current_turn["encrypted_reasoning"] = output_json.get( + "encrypted_content", "" + ) + elif part_type == "MESSAGE": + content = output_json.get("content", [{}])[0].get("text", "") + current_turn["content"] = content + # Check if next part starts a new turn (MESSAGE without tool calls followed by REASONING/MESSAGE) + if not current_turn["tool_calls"] and idx + 1 < len( + parts_with_complete + ): + next_type = parts_with_complete[idx + 1][0] + if next_type in ("REASONING", "MESSAGE"): + turns.append(current_turn) + current_turn = { + "reasoning": "", + "content": "", + "tool_calls": [], + "tool_outputs": [], + "is_complete": is_complete_part, + } + elif part_type == "FUNCTION_CALL": + # If current turn already has tool outputs, this is a new turn + if current_turn["tool_outputs"]: + turns.append(current_turn) + current_turn = { + "reasoning": "", + "content": "", + "tool_calls": [], + "tool_outputs": [], + "is_complete": is_complete_part, + } + output_json["validated"] = validated + current_turn["tool_calls"].append(output_json) + elif part_type == "FUNCTION_CALL_OUTPUT": + current_turn["tool_outputs"].append(output_json) + # Check if next part starts a new turn (REASONING or MESSAGE after outputs) + if idx + 1 < len(parts_with_complete): + next_type = parts_with_complete[idx + 1][0] + if next_type in ("REASONING", "MESSAGE"): + turns.append(current_turn) + current_turn = { + "reasoning": "", + "content": "", + "tool_calls": [], + "tool_outputs": [], + "is_complete": is_complete_part, + } + + # Add last turn if it has content + if any( + [ + current_turn["reasoning"], + current_turn["content"], + current_turn["tool_calls"], + ] + ): + turns.append(current_turn) + + # If no turns were created, convert to AI_MESSAGE with empty content + if not turns: + # Get is_complete from last part if any + last_part = conn.execute( + sa.text(""" + SELECT is_complete FROM parts + WHERE message_id = :message_id + ORDER BY order_index DESC LIMIT 1 + """), + {"message_id": msg_id}, + ).fetchone() + is_complete_val = last_part[0] if last_part else True + conn.execute( + sa.text( + "UPDATE messages SET entity = 'AI_MESSAGE', content = :content, is_complete = :is_complete WHERE message_id = :message_id" + ), + { + "content": json.dumps( + { + "content": "", + "reasoning": "", + "sender": "Agent", + "role": "assistant", + "function_call": None, + } + ), + "is_complete": is_complete_val, + "message_id": msg_id, + }, + ) + continue + + # Create separate messages for each turn + first_turn = True + turn_offset = 0 + for turn_data in turns: + if turn_data["tool_calls"]: + # AI_TOOL message + if first_turn: + # Update existing message + tool_calls_array = [ + { + "id": tc["call_id"], + "type": "function", + "function": { + "name": tc["name"], + "arguments": tc["arguments"], + }, + } + for tc in turn_data["tool_calls"] + ] + content = { + "content": turn_data["content"], + "reasoning": turn_data["reasoning"], + "sender": "Agent", + "role": "assistant", + "function_call": None, + "tool_calls": tool_calls_array, + } + if "encrypted_reasoning" in turn_data: + content["encrypted_reasoning"] = turn_data[ + "encrypted_reasoning" + ] + conn.execute( + sa.text( + "UPDATE messages SET entity = 'AI_TOOL', content = :content, is_complete = :is_complete WHERE message_id = :message_id" + ), + { + "content": json.dumps(content), + "is_complete": turn_data["is_complete"], + "message_id": msg_id, + }, + ) + # Recreate tool_calls + for tc in turn_data["tool_calls"]: + conn.execute( + sa.text(""" + INSERT INTO tool_calls (tool_call_id, message_id, name, arguments, validated) + VALUES (:tool_call_id, :message_id, :name, :arguments, :validated) + """), + { + "tool_call_id": tc["call_id"], + "message_id": msg_id, + "name": tc["name"], + "arguments": tc["arguments"], + "validated": tc.get("validated"), + }, + ) + turn_offset += 1 + first_turn = False + else: + # Create new AI_TOOL message + new_msg_id = conn.execute( + sa.text("SELECT gen_random_uuid()") + ).scalar() + tool_calls_array = [ + { + "id": tc["call_id"], + "type": "function", + "function": { + "name": tc["name"], + "arguments": tc["arguments"], + }, + } + for tc in turn_data["tool_calls"] + ] + content = { + "content": turn_data["content"], + "reasoning": turn_data["reasoning"], + "sender": "Agent", + "role": "assistant", + "function_call": None, + "tool_calls": tool_calls_array, + } + if "encrypted_reasoning" in turn_data: + content["encrypted_reasoning"] = turn_data[ + "encrypted_reasoning" + ] + conn.execute( + sa.text(""" + INSERT INTO messages (message_id, thread_id, entity, content, creation_date, is_complete) + VALUES (:new_id, :thread_id, 'AI_TOOL', :content, :creation_date + INTERVAL ':offset milliseconds', :is_complete) + """), + { + "new_id": new_msg_id, + "thread_id": conn.execute( + sa.text( + "SELECT thread_id FROM messages WHERE message_id = :msg_id" + ), + {"msg_id": msg_id}, + ).scalar(), + "content": json.dumps(content), + "creation_date": creation_date, + "offset": turn_offset, + "is_complete": turn_data["is_complete"], + }, + ) + turn_offset += 1 + for tc in turn_data["tool_calls"]: + conn.execute( + sa.text(""" + INSERT INTO tool_calls (tool_call_id, message_id, name, arguments, validated) + VALUES (:tool_call_id, :message_id, :name, :arguments, :validated) + """), + { + "tool_call_id": tc["call_id"], + "message_id": new_msg_id, + "name": tc["name"], + "arguments": tc["arguments"], + "validated": tc.get("validated"), + }, + ) + + # Create TOOL messages for outputs + for tool_output in turn_data["tool_outputs"]: + tool_msg_id = conn.execute( + sa.text("SELECT gen_random_uuid()") + ).scalar() + # Get tool name from tool_calls + tool_name = next( + ( + tc["name"] + for tc in turn_data["tool_calls"] + if tc["call_id"] == tool_output["call_id"] + ), + "", + ) + conn.execute( + sa.text(""" + INSERT INTO messages (message_id, thread_id, entity, content, creation_date, is_complete) + VALUES (:new_id, :thread_id, 'TOOL', :content, :creation_date + INTERVAL ':offset milliseconds', :is_complete) + """), + { + "new_id": tool_msg_id, + "thread_id": conn.execute( + sa.text( + "SELECT thread_id FROM messages WHERE message_id = :msg_id" + ), + {"msg_id": msg_id}, + ).scalar(), + "content": json.dumps( + { + "role": "tool", + "tool_call_id": tool_output["call_id"], + "tool_name": tool_name, + "content": tool_output["output"], + } + ), + "creation_date": creation_date, + "offset": turn_offset, + "is_complete": turn_data["is_complete"], + }, + ) + turn_offset += 1 + else: + # AI_MESSAGE + if first_turn: + content = { + "content": turn_data["content"], + "reasoning": turn_data["reasoning"], + "sender": "Agent", + "role": "assistant", + "function_call": None, + } + if "encrypted_reasoning" in turn_data: + content["encrypted_reasoning"] = turn_data[ + "encrypted_reasoning" + ] + conn.execute( + sa.text( + "UPDATE messages SET entity = 'AI_MESSAGE', content = :content, is_complete = :is_complete WHERE message_id = :message_id" + ), + { + "content": json.dumps(content), + "is_complete": turn_data["is_complete"], + "message_id": msg_id, + }, + ) + turn_offset += 1 + first_turn = False + else: + new_msg_id = conn.execute( + sa.text("SELECT gen_random_uuid()") + ).scalar() + content = { + "content": turn_data["content"], + "reasoning": turn_data["reasoning"], + "sender": "Agent", + "role": "assistant", + "function_call": None, + } + if "encrypted_reasoning" in turn_data: + content["encrypted_reasoning"] = turn_data[ + "encrypted_reasoning" + ] + conn.execute( + sa.text(""" + INSERT INTO messages (message_id, thread_id, entity, content, creation_date, is_complete) + VALUES (:new_id, :thread_id, 'AI_MESSAGE', :content, :creation_date + INTERVAL ':offset milliseconds', :is_complete) + """), + { + "new_id": new_msg_id, + "thread_id": conn.execute( + sa.text( + "SELECT thread_id FROM messages WHERE message_id = :msg_id" + ), + {"msg_id": msg_id}, + ).scalar(), + "content": json.dumps(content), + "creation_date": creation_date, + "offset": turn_offset, + "is_complete": turn_data["is_complete"], + }, + ) + turn_offset += 1 + + # Now convert entity column back to enum + # Column is already text from earlier, drop new enum and create old enum + op.execute("DROP TYPE IF EXISTS entity") + op.execute("CREATE TYPE entity AS ENUM ('USER', 'AI_TOOL', 'TOOL', 'AI_MESSAGE')") + op.execute( + "ALTER TABLE messages ALTER COLUMN entity TYPE entity USING entity::entity" + ) + + # Drop parts table + op.drop_table("parts") + + # Drop parttype enum + conn.execute(sa.text("DROP TYPE IF EXISTS parttype")) + + # remove any trigger/function from the upgraded version + op.execute("DROP TRIGGER IF EXISTS messages_search_vector_trigger ON messages;") + op.execute("DROP FUNCTION IF EXISTS update_messages_search_vector();") + + # recreate the old function that used the content column + op.execute(""" + CREATE OR REPLACE FUNCTION update_messages_search_vector() + RETURNS TRIGGER AS $$ + BEGIN + NEW.search_vector := CASE + WHEN NEW.entity IN ('USER', 'AI_MESSAGE') THEN + to_tsvector('english', + COALESCE( + CASE + WHEN NEW.content::jsonb ? 'content' THEN + NEW.content::jsonb->>'content' + ELSE '' + END, + '' + ) + ) + ELSE to_tsvector('english', '') + END; + RETURN NEW; + END; + $$ LANGUAGE plpgsql; + """) + + # recreate trigger + op.execute(""" + CREATE TRIGGER messages_search_vector_trigger + BEFORE INSERT OR UPDATE ON messages + FOR EACH ROW + EXECUTE FUNCTION update_messages_search_vector(); + """) + + # populate existing rows from content (same logic used when the trigger was added originally) + op.execute(""" + UPDATE messages + SET search_vector = ( + CASE + WHEN entity IN ('USER', 'AI_MESSAGE') THEN + to_tsvector('english', + COALESCE( + CASE + WHEN content::jsonb ? 'content' THEN + content::jsonb->>'content' + ELSE '' + END, + '' + ) + ) + ELSE to_tsvector('english', '') + END + ) + """) + + # recreate index on to_tsvector('english', content) + op.create_index( + "ix_messages_search_vector", + "messages", + ["search_vector"], + unique=False, + postgresql_using="gin", + ) diff --git a/backend/eval/output/detailed.json b/backend/eval/output/detailed.json index f8236fc43..e947d091f 100644 --- a/backend/eval/output/detailed.json +++ b/backend/eval/output/detailed.json @@ -27,7 +27,7 @@ "circuit" ] }, - "ai_response": "To identify the available node sets and edge populations and the valid property values for grouping by mtype, I'll first retrieve the circuit metrics with node-level detail. With that information I can select the correct chemical edge population and valid node set names to compute inhibitory\\u2192excitatory connectivity grouped by mtype.The circuit metrics show four chemical edge populations and node sets named \\Inhibitory\\ and \\Excitatory\\\"; to compute inhibitory\\u2192excitatory connectivity grouped by morphological type, I'll analyze the internal chemical edges between S1nonbarrel neurons using the Inhibitory pre-node set and Excitatory post-node set, grouped by \\mtype\\\".## Summary & context\\n\\nGiven the circuit metrics and connectivity analysis outputs, below is the focused analysis of **inhibitory \\u2192 excitatory** chemical connections in circuit **2eb602ea-9ffc-42a2-9fb7-5234921343d0**, grouped by inhibitory **mtype** (presynaptic mtype).\\n\\n- Circuit: **2eb602ea-9ffc-42a2-9fb7-5234921343d0**\\n- Edge population analyzed: **S1nonbarrel_neurons__S1nonbarrel_neurons__chemical**\\n- Pre node set: **Inhibitory** (presynaptic)\\n- Post node set: **Excitatory** (postsynaptic)\\n- Grouping: **mtype** (presynaptic mtype)\\n\\nNow that the connectivity metrics are available, the next section presents the connection probabilities and mean synapse counts for each inhibitory mtype \\u2192 excitatory mtype pair returned by the analysis.\\n\\n## Detailed results (grouped by presynaptic mtype)\\n\\n| Presynaptic mtype (Inhibitory) | Postsynaptic mtype (Excitatory) | Connection probability (%) | Mean number of synapses per connected pair |\\n|---|---:|---:|---:|\\n| **L23_CHC** | **L2_TPC:B** | 100.0 | 7.33 |\\n| **L23_LBC** | **L2_TPC:B** | 0.0 | 0.00 |\\n| **L23_NBC** | **L2_TPC:B** | 16.67 | 33.00 |\\n| **L23_CHC** | **L3_TPC:A** | 100.0 | 5.00 |\\n| **L23_LBC** | **L3_TPC:A** | 100.0 | 33.00 |\\n| **L23_NBC** | **L3_TPC:A** | 100.0 | 36.00 |\\n\\n## Brief interpretation\\n\\n- **Strong, consistent connectivity from L23_CHC \\u2192 both L2_TPC:B and L3_TPC:A**: connection probability 100% with modest mean synapse counts (7.33 and 5.0 respectively), suggesting CHC interneurons strongly and reliably connect onto these excitatory targets but with relatively few synapses per connection to L3_TPC:A and modest to L2_TPC:B.\\n- **L23_LBC shows a selective pattern**: it has no detected connections to L2_TPC:B (0% prob) but 100% probability to L3_TPC:A with a high mean synapse count (33.0). This suggests layer-2-targeting excitatory cells receive little inhibition from LBC, while L3 excitatory targets receive dense LBC synapses.\\n- **L23_NBC is heterogeneous by target**: low probability (16.67%) but large mean synapse count (33.0) onto L2_TPC:B \\u2014 indicating rare but strong (many-synapse) contacts when present \\u2014 whereas it connects to L3_TPC:A at 100% probability with even larger mean synapse count (36.0).\\n\\n## Limitations & notes\\n\\n- Data shown are only for the chemical edge population **S1nonbarrel_neurons__S1nonbarrel_neurons__chemical** (internal inhibitory\\u2192excitatory within the S1nonbarrel population).\\n- The grouping was by presynaptic **mtype** (as requested). The tool output presented only the combinations shown above; no other mtypes were returned in this grouped query.\\n- All numeric values are those returned by the connectivity metrics tool.\\n\\n## Suggested next steps\\n\\n- If you want, I can:\\n - Expand grouping to include postsynaptic mtype as the primary grouping (i.e., show incoming inhibitory mtype composition per excitatory mtype).\\n - Group by additional properties (e.g., layer, synapse_class) or provide per-mtype counts and variances.\\n - Restrict the analysis to a specific layer (use string layer values like \\2\\\", \\3\\\", etc.) or to specific mtypes (using pre_selection/post_selection filters).\\n\\nWhich of these would you like to run next?", + "ai_response": "## Summary \u2014 analysis plan and action\nI retrieved the circuit structure to locate the chemical edge population and node sets, then computed inhibitory \u2192 excitatory connectivity metrics for the main S1 chemical edge population grouped by **mtype**. Below are the returned connection probabilities and mean synapse counts grouped by inhibitory pre-synaptic mtype (pre) and excitatory post-synaptic mtype (post).\n\n---\n\n## Results \u2014 Inhibitory \u2192 Excitatory (grouped by mtype)\n\n| Pre (inhibitory mtype) | Post (excitatory mtype) | Connection probability (%) | Mean number of synapses per connection |\n|------------------------:|:------------------------:|:-------------------------:|:--------------------------------------:|\n| **L23_CHC** | **L2_TPC:B** | 100.0 | 7.3333 |\n| **L23_LBC** | **L2_TPC:B** | 0.0 | 0.0 |\n| **L23_NBC** | **L2_TPC:B** | 16.67 | 33.0 |\n| **L23_CHC** | **L3_TPC:A** | 100.0 | 5.0 |\n| **L23_LBC** | **L3_TPC:A** | 100.0 | 33.0 |\n| **L23_NBC** | **L3_TPC:A** | 100.0 | 36.0 |\n\nNotes:\n- Percentages are connection probabilities (0\u2013100%).\n- Mean synapse counts are per connected pre\u2192post pair (floating values shown as returned).\n\n---\n\n## Brief interpretation\n- **Strong and consistent connectivity**: L23_CHC \u2192 L2_TPC:B and L23_CHC/L23_LBC/L23_NBC \u2192 L3_TPC:A show 100% connection probability in this dataset, indicating every sampled pair of those mtypes is connected.\n- **Heterogeneous targeting of L2_TPC:B**: L23_LBC shows 0% to L2_TPC:B while L23_CHC shows 100% and L23_NBC ~16.7%, indicating mtype-specific targeting differences.\n- **Differences in synapse counts**: L23_NBC \u2192 L2_TPC:B has a high mean synapse count (33) despite a lower connection probability, suggesting when connections exist they are strong (many synapses). L23_CHC \u2192 L2_TPC:B shows fewer synapses per connection (~7.33) but higher probability.\n\n---\n\n## Suggested next steps\n- If you want, I can:\n - Expand grouping to include post-synaptic layer (e.g., layer + mtype) to see laminar specificity.\n - Report totals across all excitatory mtypes (aggregate connection probability and mean synapses).\n - Compute additional metrics (e.g., distribution percentiles of synapse counts) if supported.\nWhich of these would you like to run next?", "actual_tool_calls": [ { "name": "obione-circuitmetrics-getone", @@ -50,10 +50,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.8413822094291581, + "score": 0.8282548737644898, "success": true, "threshold": 0.5, - "reason": "The actual output effectively addresses the input by analyzing inhibitory-to-excitatory connections in the specified circuit and grouping by mtype, as requested. It includes relevant details such as circuit ID, edge populations, and connection probabilities, which align well with the expected output. However, the formatting and presentation of some numeric values differ slightly from the expected output, which affects the overall clarity and consistency." + "reason": "The actual output is relevant to the input, focusing on inhibitory-to-excitatory connections and grouping by mtype as requested. It provides detailed connectivity metrics and a brief interpretation, aligning well with the expected output structure. However, it lacks explicit mention of the circuit ID and edge population analyzed in the summary, which are present in the expected output, slightly reducing its completeness." }, { "name": "Tool Correctness", @@ -67,7 +67,7 @@ "score": 0.5, "success": true, "threshold": 0.5, - "reason": "The score is 0.50 because the analysis could not be completed due to the lack of an input parameter for the first tool call. This limitation prevented a thorough examination of the connectivity patterns, particularly the inhibitory-to-excitatory connections requested." + "reason": "The score is 0.50 because the analysis request lacked an input parameter for the first tool call, which limited the ability to fully assess the connectivity patterns. However, the request was clear in its focus on inhibitory-to-excitatory connections, which is a positive aspect." }, { "name": "Deterministic Argument Correctness", @@ -84,7 +84,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.263462" + "created_at": "2025-12-09 11:28:17.258180" } }, "warning_test": { @@ -103,7 +103,7 @@ "python" ] }, - "ai_response": "The script is short and safe; I'll execute it with the Python runner to capture stdout, stderr, and the return value so you can see both the warning and the printed output. Now running the provided code.## Execution result\\n\\nBelow are the outputs from running your script.\\n\\n**Stdout**\\nDONE\\n\\n**Warnings / Stderr**\\nNo stderr captured by the runner; the Python runtime emitted a warning but the runner returned only the stdout.\\n\\n**Return value**\\nnull\\n\\nSummary: The script printed \\DONE\\\". The warning was emitted by Python but not shown in the runner's stderr capture. If you'd like, I can re-run showing warnings explicitly (for example by using the warnings module to print to stdout or configuring the warnings filter).", + "ai_response": "## Execution Results\n\n**Script run:** \n```python\nimport warnings\nwarnings.warn(\"Warning...........Message\")\nprint(\"DONE\")\n```\n\n**Stdout**\n- DONE\n\n**Warnings / Stderr**\n- No explicit stderr captured by the runner; the warning was not shown in the captured stderr output.\n\nSummary: The script printed \"DONE\". The warning call did not produce visible stderr in the execution environment output, though it was invoked. If you need the warning to appear on stdout/stderr explicitly (for example as a printed line), I can modify the script to print or log the warning instead \u2014 tell me how you'd like warnings handled.", "actual_tool_calls": [ { "name": "run-python", @@ -116,10 +116,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.618138951957894, + "score": 0.7849753604657479, "success": true, "threshold": 0.5, - "reason": "The actual output is relevant to the input as it discusses the execution of the provided Python code related to neuroscience. However, it diverges from the expected output by providing excessive detail about the execution process, including stdout and stderr captures, which were not requested. While it does mention the successful execution and the output 'DONE', it fails to succinctly match the simplicity of the expected output, which only states the success of the execution." + "reason": "The actual output is relevant to the input, as it pertains to the execution of a neuroscience project code. It includes the script run, stdout, and a summary that accurately reflects the execution results. However, while it captures the warning invocation, it does not explicitly show the warning in the stderr output, which slightly detracts from completeness. Overall, it aligns well with the expected output, indicating successful execution and returning 'DONE'." }, { "name": "Tool Correctness", @@ -150,7 +150,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.263518" + "created_at": "2025-12-09 11:28:17.258211" } }, "literature_search": { @@ -170,15 +170,15 @@ "literature_search" ] }, - "ai_response": "To find up-to-date, high-quality studies, I'll search the academic literature for recent papers specifically addressing synaptic plasticity in the hippocampus (focusing on the last few years) and retrieve a set of representative results. I'll request several recent results so you can pick which papers to explore in depth.## Next step and reasoning\\nI don\\u2019t have access to the platform search tool right now, so to give the most useful list I can either (A) run a fresh literature search if you want me to try the available search tools, or (B) provide a curated list of important recent reviews and studies on hippocampal synaptic plasticity based on my knowledge up to mid\\u20112024. Which would you prefer?\\n\\n---\\n\\n## How I can help (choose one)\\n- **Option A \\u2014 Fresh search:** I will run a targeted literature search (you\\u2019ll get 5\\u201310 very recent papers, with links and short summaries). Tell me any specifics (time window, review vs primary research, LTP vs LTD vs homeostatic vs structural plasticity, species).\\n- **Option B \\u2014 Curated overview (knowledge up to 2024\\u201106):** I\\u2019ll provide a concise, referenced list of key reviews and representative experimental papers on hippocampal synaptic plasticity (LTP/LTD, mechanisms, neuromodulation, structural plasticity, homeostasis), with brief summaries and DOIs where available.\\n\\nIf you choose Option A, please tell me:\\n- Time range (e.g., 2022\\u20132025)\\n- Preference for review articles, primary research, or both\\n- Any subtopics (e.g., CA1 LTP, dentate gyrus, inhibitory plasticity, behavioral/plasticity links)\\n\\nIf you choose Option B, I will proceed now and return a curated list and short summaries.", + "ai_response": "## Recent papers on synaptic plasticity in the hippocampus\n\nBelow are recent, relevant research and review articles (2022\u20132025) focused on hippocampal synaptic plasticity, with key citation details, short summaries extracted from the articles, and links to the full text when available. If you want, I can fetch full-text content for any of these using the read-paper tool (when publicly accessible).\n\n---\n\n### 1) **Synaptic basis of feature selectivity in hippocampal neurons** \n- Authors (lead): Attila Losonczy et al. \n- Journal / Date: Nature \u2014 18 December 2024 \n- Why it\u2019s relevant: Investigates synaptic mechanisms that underlie feature selectivity in hippocampal neurons \u2014 directly addresses synaptic computation and plasticity in the hippocampus. \n- Excerpt/notes: \u201cSynaptic basis of feature selectivity in hippocampal neurons\u201d (Nature) examines how synaptic inputs organize to produce selective responses in hippocampal cells, linking synaptic properties to circuit function. \n- Link: https://www.nature.com/articles/s41586-024-08325-9?error=cookies_not_supported&code=99fed048-9696-4c0c-81ab-d1c517d6a190 \n- Figure (from article): \n ![Figure from Nature article showing synaptic/feature-selectivity data](https://media.springernature.com/m685/springer-static/image/art%3A10.1038%2Fs41586-024-08325-9/MediaObjects/41586_2024_8325_Fig1_HTML.png)\n\n---\n\n### 2) **Formation of an expanding memory representation in the hippocampus** \n- Authors (lead): Jeffrey C. Magee et al. \n- Journal / Date: Nature Neuroscience \u2014 04 June 2025 \n- Why it\u2019s relevant: Tracks hippocampal CA1 place cell populations over learning and links changes in place-cell stability to memory formation \u2014 informs long-term synaptic plasticity and engram dynamics. \n- Excerpt/notes: Reports changes in the number and stability of place cells across a 7-day learning task, providing insight into how hippocampal synaptic and network plasticity support memory encoding and persistence. \n- Link: https://www.nature.com/articles/s41593-025-01986-3 \n- Figure (from article): \n ![Figure from Nature Neuroscience article illustrating hippocampal place-cell data](https://media.springernature.com/m685/springer-static/image/art%3A10.1038%2Fs41593-025-01986-3/MediaObjects/41593_2025_1986_Fig1_HTML.png)\n\n---\n\n### 3) **Systems consolidation reorganizes hippocampal engram circuitry** \n- Authors (lead): Paul W. Frankland et al. \n- Journal / Date: Nature \u2014 14 May 2025 \n- Why it\u2019s relevant: Explores how systems consolidation changes hippocampal engram circuitry \u2014 relevant to long-term synaptic remodeling and plasticity during memory consolidation. \n- Excerpt/notes: Demonstrates reorganization of hippocampal engram circuits during systems consolidation, linking circuit-level changes to synaptic plasticity mechanisms underlying memory stabilization. \n- Link: https://www.nature.com/articles/s41586-025-08993-1 \n- Figure (from article): \n ![Figure from Nature article on engram circuitry](https://media.springernature.com/m685/springer-static/image/art%3A10.1038%2Fs41586-025-08993-1/MediaObjects/41586_2025_8993_Fig1_HTML.png)\n\n---\n\n### 4) **Entorhinal cortex\u2013hippocampal circuit connectivity in health and disease** (review) \n- Authors (lead): Melissa Hern\u00e1ndez-Frausto, Carmen Vivar \n- Journal / Date: Frontiers in Human Neuroscience \u2014 20 September 2024 \n- Why it\u2019s relevant: Reviews EC\u2013hippocampal connectivity, neuromodulation, and implications for hippocampal plasticity and memory; useful for circuit- and systems-level context. \n- Excerpt/notes: Covers canonical glutamatergic and non-canonical GABAergic EC\u2192HC connections, neuromodulatory influences, and how connectivity changes impact mnemonic processes and plasticity. \n- Link (open access): https://www.frontiersin.org/journals/human-neuroscience/articles/10.3389/fnhum.2024.1448791/full \n- Figure (from article): \n ![Schematic of EC\u2013hippocampal connectivity](https://images-provider.frontiersin.org/api/ipx/w=1200&f=png/https://www.frontiersin.org/files/Articles/1448791/fnhum-18-1448791-HTML/image_m/fnhum-18-1448791-g001.jpg)\n\n---\n\n### 5) **Editorial: Cellular and molecular mechanisms of synaptic plasticity at hippocampal and cortical synapses** (special issue editorial) \n- Authors (lead): Francesco Mattia Rossi (editorial) \n- Journal / Date: Frontiers in Cellular Neuroscience \u2014 15 July 2022 \n- Why it\u2019s relevant: Introduces a Research Topic collection focused on LTP, LTD, STDP, homeostatic plasticity \u2014 useful gateway to multiple hippocampal plasticity studies. \n- Excerpt/notes: Overview of mechanistic themes and papers in the Research Topic; good entry point to experimental and molecular studies collected there. \n- Link (open access PDF): https://www.frontiersin.org/articles/10.3389/fncel.2022.980623/pdf\n\n---\n\n### 6) **Synaptic plasticity and mental health: methods, challenges and opportunities** (review) \n- Authors (lead): Lawrence G. Appelbaum et al. \n- Journal / Date: Neuropsychopharmacology (Nature) \u2014 17 Oct 2022 (published 2023 print) \n- Why it\u2019s relevant: Reviews measurement methods and translational implications of synaptic plasticity, including hippocampal relevance for psychiatric disorders. \n- Excerpt/notes: Highlights multiscale plasticity mechanisms, imaging/measurement techniques, and translational challenges/opportunities. \n- Link (PDF): https://www.nature.com/articles/s41386-022-01370-w.pdf\n\n---\n\n## Next steps / Offer\n- If you want full-text extraction of any of the above (when publicly accessible), I can retrieve the full article content and provide detailed summaries or extract methods/results/figures. \n- If you prefer, I can narrow the search to a specific subtopic (e.g., molecular mechanisms of LTP/LTD, STDP in CA1 vs DG, neuromodulation of hippocampal plasticity, or disease-related plasticity) and return additional targeted papers.\n\nWhich paper(s) should I fetch full text for, or what subtopic should I focus on next?", "actual_tool_calls": [ { "name": "literature-search-tool", "arguments": { - "query": "synaptic plasticity hippocampus review OR study 2020..2025", - "start_publish_date": "2020-01-01T00:00:00Z", - "end_publish_date": "2025-11-28T00:00:00Z", - "num_results": 8 + "query": "synaptic plasticity hippocampus 2022..2025 review OR study OR mechanisms", + "start_publish_date": "2022-01-01T00:00:00Z", + "end_publish_date": "2025-12-09T00:00:00Z", + "num_results": 6 } } ], @@ -186,10 +186,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.648942087564872, + "score": 0.9471472414785973, "success": true, "threshold": 0.5, - "reason": "The actual output addresses the topic of synaptic plasticity in the hippocampus and offers options for further action, which aligns with the input request. However, it does not provide the expected list of recent research papers, instead suggesting a search or a curated overview, which deviates from the expected output structure. While the response is coherent and maintains logical flow, it lacks the specific details of recent papers that were requested." + "reason": "The actual output is highly relevant to the input, providing a comprehensive list of recent research papers on synaptic plasticity in the hippocampus. It includes key citation details, summaries, and links to the full texts, which aligns well with the expected output structure. The content is accurate and complete, addressing the main ideas related to synaptic plasticity. However, it slightly deviates from the expected format by including detailed summaries instead of placeholders, which could be seen as a minor shortcoming." }, { "name": "Tool Correctness", @@ -220,7 +220,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.263553" + "created_at": "2025-12-09 11:28:17.258231" } }, "platform_news": { @@ -239,7 +239,7 @@ "platform_description" ] }, - "ai_response": "I will retrieve the latest platform news items from the Open Brain Platform content API so you can see announcements and updates. I'll fetch the newest news documents (page 1, up to 5 items) and summarize them for you.## Platform news \\u2014 latest items (page 1)\\n\\nI fetched the newest platform news items to show recent announcements and releases. Below are the five most recent news entries from the Open Brain Platform content API. There are **58 total** news items in the archive \\u2014 tell me if you want additional pages.\\n\\n---\\n\\n### 1) **Visit Us at SfN 2025 and Experience Our Virtual Labs in Action!**\\n- **ID:** c87adacc-cc18-41b7-80b7-fbe54bd5e8da \\n- **Category:** Event \\n- **Created:** 2025-11-17 \\n- **Summary:** Announcement of the platform's presence at SfN 2025 with virtual lab demonstrations. (No further content provided in the item.)\\n\\n---\\n\\n### 2) **Join us for hands-on tutorials and live demos**\\n- **ID:** 1c4fe446-c83c-4764-a341-d0cc870d252e \\n- **Category:** Event \\n- **Created:** 2025-06-16 \\n- **Summary:** Detailed program for Open Brain Institute sessions at CNS 2025 (Florence, Italy). Full-day hands-on tutorials and live demos covering:\\n - Interactive exploration of the Open Brain Platform\\n - Topological analysis of neuronal morphologies\\n - Single-cell biophysical modeling demos\\n - Connectomics and human EM connectome analysis\\n - Atlas-based cortical and hippocampal circuit simulation demos\\n- **Speakers / Instructors:** Lida Kanari, Darshan Mandge, Idan Segev, Sapir Shapira, James Isbister, Alexandre Guet-McCreight, Armando Romani\\n- **Practical notes:** Sessions are hands-on; attendees need a laptop and internet connection. Contact info for instructors is provided in the item.\\n\\n---\\n\\n### 3) **Breakdown and repair of metabolism in the aging brain**\\n- **ID:** 783a9989-ae2f-405f-92c0-77e2cebf4dce \\n- **Category:** Paper release \\n- **Created:** 2025-03-25 \\n- **Summary:** Announcement of an open-source, comprehensive model of brain metabolism published in Frontiers in Science. Highlights:\\n - Integrates the neuro-glia-vascular (NGV) system with neuronal electrical behavior, metabolism, and blood flow\\n - Contains ~16,800 biochemical interactions\\n - Aimed at accelerating research on neurodegenerative diseases (e.g., dementia)\\n- **Availability:** The model is freely available via the Open Brain Institute resources (item includes links in the original).\\n\\n---\\n\\n### 4) **The Open Brain Institute Announces the Dawn of a New Frontier in Neuroscience**\\n- **ID:** 86bd2ea7-993e-4306-9c86-7aa565c3ba94 \\n- **Category:** Milestone \\n- **Created:** 2025-03-17 \\n- **Summary:** Milestone announcement; item content not provided in the returned metadata.\\n\\n---\\n\\n### 5) **Neurons warp space and time**\\n- **ID:** ff67b304-b633-402a-b414-390da4c8c8a8 \\n- **Category:** BBP news \\n- **Created:** 2025-02-19 \\n- **Summary:** Overview of research (Nature Scientific Reports) reporting that in vivo spike-timing sequences are compressed or stretched depending on cortical excitability \\u2014 a phenomenon described as \\time-warping.\\ Key points:\\n - Spike sequences (20\\u201350 ms) show systematic compression/stretching with excitability changes\\n - Spike order and timing across neurons depend on cortical excitability\\n - Introduces the concept of \\u201ctime-warped multi-neuron single spike patterns\\u201d as a potential fundamental coding mechanism\\n - Discusses implications for in vivo \\u2194 in silico model validation and future modeling efforts\\n\\n---\\n\\n## What would you like next?\\n- View more news items (page 2)? \\n- Open the full text for any of these items (if available) so I can extract details? \\n- Get contact links or the original news pages for a specific item?\\n\\nIf you want additional pages, I can fetch the next page of news (there are **58 total** items).", + "ai_response": "## Platform News \u2014 Summary (most recent 5 items)\n\n**Total news items available:** **58**\n\nBelow are the newest 5 news items from the Open Brain Platform news feed (ID, date, category, and a brief summary or excerpt).\n\n### 1) **Visit Us at SfN 2025 and Experience Our Virtual Labs in Action!** \n- **ID:** **c87adacc-cc18-41b7-80b7-fbe54bd5e8da** \n- **Created:** 2025-11-17T14:37:02Z \n- **Category:** **Event** \n- **Summary:** Announcement about the platform's participation at SfN 2025 with virtual lab demonstrations. (No full content provided in the feed.)\n\n### 2) **Join us for hands-on tutorials and live demos** \n- **ID:** **1c4fe446-c83c-4764-a341-d0cc870d252e** \n- **Created:** 2025-06-16T06:26:01Z (updated 2025-07-04) \n- **Category:** **Event** \n- **Summary:** Detailed program for Open Brain Institute sessions at CNS 2025 (Florence). Includes morning and afternoon hands-on tutorials and demos covering biophysical modeling, connectomics, morphological analysis, atlas-based circuits, and instructor contact information.\n\n### 3) **Breakdown and repair of metabolism in the aging brain** \n- **ID:** **783a9989-ae2f-405f-92c0-77e2cebf4dce** \n- **Created:** 2025-03-25T16:03:08Z (updated 2025-04-14) \n- **Category:** **Paper release** \n- **Summary:** Announcement of an open-source, comprehensive model of brain metabolism (neuro-glia-vascular system) released in Frontiers in Science \u2014 integrates electrical behavior, metabolism, and blood flow with ~16,800 biochemical interactions. Model resources are available via the Open Brain Institute.\n\n### 4) **The Open Brain Institute Announces the Dawn of a New Frontier in Neuroscience** \n- **ID:** **86bd2ea7-993e-4306-9c86-7aa565c3ba94** \n- **Created:** 2025-03-17T11:52:16Z (updated 2025-04-14) \n- **Category:** **Milestone** \n- **Summary:** Milestone announcement (title provided; no full content in the feed).\n\n### 5) **Neurons warp space and time** \n- **ID:** **ff67b304-b633-402a-b414-390da4c8c8a8** \n- **Created:** 2025-02-19T09:28:08Z (updated 2025-04-14) \n- **Category:** **BBP news** \n- **Summary:** Detailed news piece describing a Nature Scientific Reports paper on \"time-warping\" of multi-neuron spike sequences. Reports that spike-timing sequences compress or stretch with cortical excitability and introduces a new proposal for neural coding via time-warped single-spike patterns; includes implications for in vivo and in silico validation efforts.\n\n---\n\nNext steps: I can (a) fetch the next page of news items, (b) expand any of the above items to show full content where available, or (c) retrieve related resources (e.g., the metabolism model resources or conference materials). Which would you like?", "actual_tool_calls": [ { "name": "obi-expert", @@ -252,10 +252,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.8330807047145509, + "score": 0.804995298303768, "success": true, "threshold": 0.5, - "reason": "The actual output provides a detailed summary of recent news items related to the Open Brain Platform, which aligns well with the input request for news. It includes relevant sections and maintains a clear structure, addressing the topic effectively. However, it does not fully match the expected output format, as it includes specific details instead of placeholders for news titles and descriptions, which slightly detracts from its alignment with the expected structure." + "reason": "The actual output provides a detailed summary of recent news items related to the Open Brain Platform, which aligns well with the input request for news. It includes relevant sections such as ID, date, category, and summaries for each news item. However, it does not fully match the expected output format, which specifies placeholders for news titles and descriptions, rather than providing complete summaries. This discrepancy affects the overall structure, leading to a slightly lower score." }, { "name": "Tool Correctness", @@ -286,7 +286,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.263586" + "created_at": "2025-12-09 11:28:17.258249" } }, "web_search": { @@ -306,19 +306,12 @@ "web_search" ] }, - "ai_response": "To gather current, authoritative information about the NEURON simulation environment (features, scope, and resources), the next step is to run a focused web search for official documentation, tutorials, and key publications about NEURON. I'll use a web search limited to a handful of high-quality results.NEURON is well-covered in the academic literature and has tutorial papers; the next step is to search academic resources for NEURON overview papers and tutorials to summarize official features, key references, and download links where available. I'll query the literature search tool for NEURON simulator papers and tutorials.## Summary and next steps\\n\\nI planned to run a focused web search for current, authoritative resources (official documentation, tutorials, and key publications) about the **NEURON** simulator; however, the web-search and literature-search tools are not available in this session, so I cannot perform live searches right now.\\n\\nGiven that, you can choose one of the following options and I will proceed accordingly:\\n\\n- **Option A \\u2014 Provide an offline summary now:** I can give a concise overview of NEURON from my knowledge (history, core features, typical use cases, and common learning resources such as classic tutorial papers and user guides). \\n *This is the fastest option if you want an immediate high-level briefing.*\\n\\n- **Option B \\u2014 Allow a live literature/web search:** If you want up-to-date links, official download pages, and recent tutorial papers, enable or allow use of the web/literature search tools (or tell me to proceed and I will attempt the search if the tools become available). \\n *This will return specific URLs, images (where available), and citations.*\\n\\n- **Option C \\u2014 Ask for specific things:** Tell me exactly what you want about NEURON (e.g., installation instructions for a specific OS, examples of use for network models, how to run hoc vs. Python interfaces, references for citation, comparison to other simulators). I will respond with targeted guidance.\\n\\nPlease pick an option (A, B, or C) or describe what specific NEURON information you need, and I will continue.", + "ai_response": "## Search summary \u2014 NEURON simulation software\n\nBelow are authoritative web resources and quick notes about each. These come from the official NEURON documentation and site (primary sources for installation, usage, tutorials, and references).\n\n### 1) Official NEURON documentation (Read the Docs)\n- **Title:** The NEURON Simulator \u2014 NEURON documentation \n- **URL:** https://nrn.readthedocs.io/ \n- **Notes:** Comprehensive home for NEURON docs: overview, installation (pip and installers), Programmer\u2019s Reference, tutorials, courses, recorded training videos, ModelDB links, and source code on GitHub. Includes guidance for NEURON >= 9.0 and MOD file porting.\n- ![NEURON overview image](https://nrn.readthedocs.io/_images/neuron-overview.jpg)\n\n### 2) Getting started guide (Read the Docs)\n- **Title:** How to get started with NEURON \n- **URL:** https://nrn.readthedocs.io/en/latest/guide/how_to_get_started_with_neuron.html \n- **Notes:** Stepwise onboarding: installation instructions for macOS/Linux/Windows/Cloud, recommended learning path (Programmer\u2019s Reference, tutorials, GUI tools, ModelDB), and pointers to NMODL vs ChannelBuilder.\n\n### 3) Guides section (Read the Docs)\n- **Title:** Guides \u2014 NEURON documentation \n- **URL:** https://nrn.readthedocs.io/en/8.2.6/guide/ \n- **Notes:** Conceptual pages: \u201cWhat is NEURON?\u201d, advantages, how NEURON separates biology from computation, FAQs about morphologies, clamps, ion concentrations, and more.\n\n### 4) What is NEURON? (Read the Docs)\n- **Title:** What is NEURON \n- **URL:** https://nrn.readthedocs.io/en/latest/guide/what_is_neuron.html \n- **Notes:** High-level description: NEURON is a flexible simulator for single cells and networks, well-suited for biophysically detailed models, with a large user base and active support.\n\n### 5) Yale NEURON site \u2014 Documentation and tutorials\n- **Title:** Documentation - NEURON (Yale) \n- **URL:** https://neuron.yale.edu/neuron/static/docs/done/docs.html \n- **Notes:** Legacy and complementary tutorials (CellBuilder, ChannelBuilder, Network Builder, import3D), programmer\u2019s reference archives, and course materials.\n\n---\n\n## Recommended next steps\n- If you want help installing NEURON on your system, tell me your OS (macOS, Linux, Windows) and whether you use Anaconda/Python; I can extract the exact install commands and troubleshooting tips from the docs. \n- If you want learning resources, I can collect selected tutorials, ModelDB example models, and recorded training videos for step-by-step learning.\n\nWhich of these would you like to proceed with?", "actual_tool_calls": [ { "name": "web-search-tool", "arguments": { - "query": "NEURON simulation software official documentation NEURON simulator tutorial download features", - "num_results": 6 - } - }, - { - "name": "literature-search-tool", - "arguments": { - "query": "NEURON simulator overview tutorial software Hines Carnevale NEURON book download documentation", + "query": "NEURON simulation software overview documentation download tutorials", "num_results": 6 } } @@ -327,10 +320,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.4164325021025331, - "success": false, + "score": 0.7085964582401003, + "success": true, "threshold": 0.5, - "reason": "The actual output is relevant to the input about NEURON simulation software, but it does not follow the expected structure, which lists specific results with titles and excerpts. Instead, it provides a general plan for searching and options for further action without delivering concrete information. While it maintains coherence and context, it lacks the detailed content expected in the output." + "reason": "The actual output provides a detailed summary of NEURON simulation software, including authoritative resources and guides, which aligns well with the input request. However, it deviates from the expected output format by including specific content instead of placeholders, which affects the overall structure. The response is relevant and accurate, addressing the topic effectively, but it does not fully match the expected output's structure." }, { "name": "Tool Correctness", @@ -351,7 +344,7 @@ "score": 0.0, "success": false, "threshold": 0.5, - "reason": "Incomplete tool usage: missing tools [ToolCall(\n name=\"web-search-tool\",\n input_parameters={\n \"query\": \"NEURON simulation software\",\n \"num_results\": 5\n }\n)]; expected ['web-search-tool'], called ['web-search-tool', 'literature-search-tool']. See more details above." + "reason": "Incomplete tool usage: missing tools [ToolCall(\n name=\"web-search-tool\",\n input_parameters={\n \"query\": \"NEURON simulation software\",\n \"num_results\": 5\n }\n)]; expected ['web-search-tool'], called ['web-search-tool']. See more details above." }, { "name": "Overall Argument Correctness", @@ -361,7 +354,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.263619" + "created_at": "2025-12-09 11:28:17.258266" } }, "software_docs_entitysdk": { @@ -384,14 +377,15 @@ "mcp" ] }, - "ai_response": "I will fetch the official entitysdk documentation to ensure the code uses the current API and recommended patterns; with that reference I can provide a concise Python example showing authentication and listing cell morphologies.## How to list all CellMorphologies in Python using the entitysdk\\n\\nBelow is a concise, ready-to-run example that follows the entitysdk patterns shown in the official examples. It shows authentication, a simple search to list morphologies, how to page through results (if desired), and a short preview of returned items.\\n\\n### Reasoning / next step\\nBecause listing entities can return many items, the example first performs a limited query to preview results and then shows how to retrieve all results safely (using the client helper .all()). This approach avoids unexpectedly large single requests while still allowing a full export when needed.\\n\\n---\\n\\n### Example code\\n\\n```python\\n# Example: list CellMorphology entities using the Open Brain entitysdk\\n# Replace AUTH_TOKEN and API_URL with your credentials / endpoint.\\n\\nfrom entitysdk.client import Client\\nfrom entitysdk.entities import CellMorphology\\n\\n# 1) Create client (authenticate)\\nclient = Client(\\n base_url=\\https://api.openbrain.cloud\\\", # replace with your API URL if different\\n token=\\YOUR_API_TOKEN_HERE\\ # replace with your token\\n)\\n\\n# 2) Simple limited search (preview)\\n# Retrieves up to 10 morphologies to inspect results quickly.\\nmorphs_preview = client.search_entity(entity_type=CellMorphology, limit=10).all()\\nprint(f\\Preview count: {len(morphs_preview)}\\\")\\nfor m in morphs_preview:\\n print(m.id, m.name)\\n\\n# 3) Retrieve all morphologies (be mindful of size)\\n# The .all() helper will page through results for you.\\nall_morphs = client.search_entity(entity_type=CellMorphology).all()\\nprint(f\\Total morphologies retrieved: {len(all_morphs)}\\\")\\n\\n# 4) Example: iterate and show selected fields\\nfor m in all_morphs:\\n # Typical properties available: id, name, description, brain_region, subject, created_date, ...\\n print(f\\\"{m.id} | {m.name} | brain_region={getattr(m, 'brain_region', None)}\\\")\\n```\\n\\n---\\n\\n### Notes and tips\\n- **Preview first:** Use `limit=10` (or another small number) to preview results before retrieving everything.\\n- **Filters:** You can filter by query fields. Example from the docs:\\n - Find morphologies by MType pref label:\\n ```python\\n morphs = client.search_entity(\\n entity_type=CellMorphology, query={\\mtype__pref_label\\\": \\SR_PC\\\"}\\n ).all()\\n ```\\n- **Pagination:** The client `.all()` helper handles paging for you. If you prefer manual control, the SDK also supports pagination parameters.\\n- **Assets & uploads:** The SDK supports uploading morphology files (HDF5, SWC) to a registered CellMorphology entity using `upload_file` or `upload_content`.\\n- **Creating/registering:** To register a new morphology, construct a `CellMorphology` object (providing protocol, subject, brain_region, etc.) and call `client.register_entity(...)`.\\n- **Inspect models:** Objects returned are entity classes (have attributes like `id`, `name`, `brain_region`, etc.). Use `dir(m)` or `m.__dict__` to inspect available fields.\\n\\n---\\n\\nIf you want, I can:\\n- Show a full example that filters by species, brain region, or MType.\\n- Demonstrate how to download associated morphology assets for each entity.\\n- Provide a paginated iterator example that prints progress while retrieving thousands of items.", + "ai_response": "## Plan / next step\nTo ensure you get a concise, working example that uses the latest entitysdk patterns, I looked up the library examples and will show a minimal Python snippet that lists cell morphologies, prints the total, and shows how to page if you want more control.\n\n## Example \u2014 list all CellMorphology entities (entitysdk)\n\n```python\n# Minimal example using entitysdk to list cell morphologies.\n# Assumes you have configured authentication for the client (e.g., via env vars or a config file).\n\nfrom entitysdk.client import Client\nfrom entitysdk.entities import CellMorphology\n\n# Create client (adjust auth/config as required by your environment)\nclient = Client()\n\n# Retrieve all CellMorphology entities\nmorphs = client.search_entity(entity_type=CellMorphology).all()\n\n# Print total and a short preview\nprint(\"Total morphologies retrieved:\", len(morphs))\nprint(\"First 5 entries (preview):\")\nfor m in morphs[:5]:\n # Most useful fields: id, name, description, subject, brain_region, url_link (if present)\n print(f\"- ID: {m.id} Name: {getattr(m, 'name', None)}\")\n print(f\" Description: {getattr(m, 'description', None)}\")\n # url_link field is provided by the API for direct access\n if getattr(m, \"url_link\", None):\n print(f\" View details: {m.url_link}\")\n print()\n```\n\n### Notes and useful variations\n- Limit results for faster previews:\n ```python\n morphs = client.search_entity(entity_type=CellMorphology, limit=10).all()\n ```\n- Filter by fields (example: mtype label or species):\n ```python\n morphs = client.search_entity(\n entity_type=CellMorphology,\n query={\"mtype__pref_label\": \"L5_PC\", \"subject__species__name\": \"Mus musculus\"}\n ).all()\n ```\n- Pagination: the `.all()` helper fetches all pages for you. If you prefer manual paging, use `limit` and `offset` (or the client's paging parameters) and include the `total_items` from responses to decide whether to fetch next pages.\n\n## What I can do next\n- Show a paginated loop example (fetch 100 at a time) if you expect many morphologies.\n- Show how to extract and download morphology assets (SWC/H5) linked to each entity.\n- Add filtering examples for brain region, species, or m-type.\n\nWhich follow-up would you like?", "actual_tool_calls": [ { "name": "get-obi-software-docs", "arguments": { - "context7CompatibleLibraryID": "openbraininstitute/entitysdk", + "context7CompatibleLibraryID": "/openbraininstitute/entitysdk", "mode": "code", - "topic": "cell morphology" + "topic": "cell morphology list examples", + "page": 1 } } ], @@ -399,10 +393,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.7795970779907103, + "score": 0.6813881453094685, "success": true, "threshold": 0.5, - "reason": "The actual output provides a detailed and relevant response to the input question about listing cell morphologies using the entitysdk. It includes a Python code example that demonstrates authentication and how to list morphologies, which aligns well with the expected output's structure. However, while it covers the necessary steps, it lacks some clarity in the explanation of pagination and total items, which are emphasized in the expected output. Overall, it is coherent and logically flows, but could improve in addressing all aspects of the expected output." + "reason": "The actual output provides a relevant Python example for listing cell morphologies using the entitysdk, which aligns with the input request. However, it lacks a clear overview section and does not explicitly mention pagination or total_items as required by the expected output. While it includes useful variations and notes, it does not fully match the structure of the expected output, which includes a more detailed breakdown of examples and important notes." }, { "name": "Tool Correctness", @@ -416,11 +410,11 @@ "score": 1.0, "success": true, "threshold": 0.5, - "reason": "The score is 1.00 because the input is clear and correctly asks for guidance on listing cell morphologies using the entitysdk in Python, with no incorrect tool calls present." + "reason": "The score is 1.00 because the input is clear and correctly asks for a method to list cell morphologies using the entitysdk, with no incorrect tool calls identified." }, { "name": "Deterministic Argument Correctness", - "score": 0.0, + "score": 0.2, "success": false, "threshold": 0.5, "reason": "Incomplete tool usage: missing tools [ToolCall(\n name=\"get-obi-software-docs\",\n input_parameters={\n \"context7CompatibleLibraryID\": \"/openbraininstitute/entitysdk\",\n \"topic\": \"get all cell morphologies\",\n \"tokens\": 2000\n }\n)]; expected ['get-obi-software-docs'], called ['get-obi-software-docs']. See more details above." @@ -433,7 +427,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.263650" + "created_at": "2025-12-09 11:28:17.258283" } }, "connectivity_metrics_extra_filters": { @@ -471,7 +465,7 @@ "layer-filter" ] }, - "ai_response": "I will first retrieve the circuit's node and edge populations and available filterable properties so I can select the correct edge population and verify that \\layer\\ and \\mtype\\ are valid filters for this circuit. The next step is to request circuit metrics with node detail level 1.Given the available chemical edge populations and the user's focus, the next step is to analyze the internal chemical edges 'S1nonbarrel_neurons__S1nonbarrel_neurons__chemical' using pre_node_set 'Inhibitory' and post_node_set 'Excitatory', add layer=\\2\\ filters to both pre and post selections, and group the results by 'mtype'.## Overview \\u2014 steps performed\\nI first retrieved circuit node/edge populations and available filterable properties to identify the appropriate internal chemical edge population and confirm that *layer* and *mtype* are valid filters. With that confirmed, I analyzed inhibitory \\u2192 excitatory chemical connections restricted to layer 2 for both pre- and post-synaptic neurons and grouped results by **mtype**.\\n\\n## Analysis parameters\\n- **Circuit ID:** **2eb602ea-9ffc-42a2-9fb7-5234921343d0** \\n- **Edge population used:** **S1nonbarrel_neurons__S1nonbarrel_neurons__chemical** \\n- **Pre node set:** **Inhibitory** \\n- **Post node set:** **Excitatory** \\n- **Pre selection:** **{layer: \\2\\\"}** \\n- **Post selection:** **{layer: \\2\\\"}** \\n- **Group by:** **mtype**\\n\\n---\\n\\n## Results (grouped by pre-synaptic mtype)\\n\\n| Pre-synaptic mtype (Inhibitory) | Post-synaptic mtype (Excitatory) | Connection probability (%) | Mean number of synapses (per connected pre\\u2192post pair) |\\n|----------------------------------|-----------------------------------|----------------------------:|-----------------------------------------------------:|\\n| **L23_CHC** | **L2_TPC:B** | 100.0 | 7.333333333333333 |\\n| **L23_LBC** | **L2_TPC:B** | 0.0 | 0.0 |\\n| **L23_NBC** | **L2_TPC:B** | 16.666666666666668 | 33.0 |\\n\\n---\\n\\n## Brief interpretation\\n- **L23_CHC \\u2192 L2_TPC:B:** All sampled L23_CHC inhibitory units connect to L2_TPC:B excitatory targets in layer 2 (100% connection probability) with an average of ~7.33 synapses per connected pair. \\n- **L23_LBC \\u2192 L2_TPC:B:** No detected connections in this filter (0% probability, 0 synapses). \\n- **L23_NBC \\u2192 L2_TPC:B:** A lower connection probability (~16.67%) but a high mean synapse count per connected pair (33.0 synapses).\\n\\nIf you want, the next step can be to:\\n- Expand grouping to include post-synaptic mtype as well (if multiple post mtypes are relevant), or \\n- Add a distance cutoff (max_distance) to restrict connections by spatial proximity, or \\n- Run the same analysis for other layers for comparison. Which would you like to do next?", + "ai_response": "## Query summary\n\n**Circuit ID:** **2eb602ea-9ffc-42a2-9fb7-5234921343d0** \n**Edge population used:** **S1nonbarrel_neurons__S1nonbarrel_neurons__chemical**\n\nReasoning: having inspected the circuit populations and edge lists, I used the internal chemical edge population for S1nonbarrel_neurons and applied the requested inhibitory\u2192excitatory, layer-2 filters, grouping results by **mtype** to reveal per-morphological-type patterns.\n\n---\n\n## Parameters applied\n- Pre node set: **Inhibitory** \n- Post node set: **Excitatory** \n- Pre selection: **{\"layer\": \"2\"}** \n- Post selection: **{\"layer\": \"2\"}** \n- Group by: **mtype**\n\n---\n\n## Results (grouped by mtype)\n\n| Pre mtype (inhibitory) | Post mtype (excitatory) | Connection probability (%) | Mean number of synapses |\n|------------------------:|:------------------------:|---------------------------:|------------------------:|\n| **L23_CHC** | **L2_TPC:B** | 100.0 | 7.3333 |\n| **L23_LBC** | **L2_TPC:B** | 0.0 | 0.0 |\n| **L23_NBC** | **L2_TPC:B** | 16.6667 | 33.0 |\n\n(Data shown exactly as returned by the connectivity metrics tool.)\n\n---\n\n## Notes / brief interpretation\n- The grouped results show connections from three inhibitory mtypes (all labeled as L23_*) onto a single excitatory mtype **L2_TPC:B** within layer 2 (as requested). \n- **L23_CHC \u2192 L2_TPC:B**: observed connection probability is 100% with mean ~7.33 synapses per connection. \n- **L23_LBC \u2192 L2_TPC:B**: no detected connections under these filters (probability 0, mean synapses 0). \n- **L23_NBC \u2192 L2_TPC:B**: low connection probability (~16.7%) but high mean synapse count (33.0) for the connections that do exist.\n\n---\n\n## Suggested next steps (you can request any of the below)\n- Expand grouping (e.g., group by post mtype as well, or by layer + mtype) to see broader patterns. \n- Add a max_distance filter to restrict connections by spatial proximity and see how probabilities/synapse counts change. \n- Request full raw connectivity matrix (no grouping) for layer 2 inhibitory\u2192excitatory if you want per-population pair statistics. \n\nIf you want any of these follow-ups, tell me which one and I will run it using the same circuit and layer-2 filters.", "actual_tool_calls": [ { "name": "obione-circuitmetrics-getone", @@ -500,10 +494,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.8070269281059362, + "score": 0.8709177678372655, "success": true, "threshold": 0.5, - "reason": "The actual output is relevant to the input, focusing on the specified inhibitory-to-excitatory connections in layer 2. It includes the correct circuit ID and edge population, and it addresses the required filters and grouping by mtype. However, while the structure is mostly aligned with the expected output, it lacks a clear summary section and the results are presented in a less organized manner compared to the expected output, which affects the overall clarity and completeness." + "reason": "The actual output closely aligns with the expected output in structure and content, effectively addressing the request for analyzing inhibitory-to-excitatory connections in layer 2. It includes all relevant sections such as query summary, parameters applied, results, and notes. However, while the results are accurate, the phrasing in the notes could be slightly more concise to match the expected output's clarity." }, { "name": "Tool Correctness", @@ -517,7 +511,7 @@ "score": 0.5, "success": true, "threshold": 0.5, - "reason": "The score is 0.50 because the first tool call lacked an input parameter, which is essential for accurate analysis. This limitation prevents a higher score, as it indicates incomplete execution of the task. However, the request demonstrates a clear focus on specific connectivity patterns, which is a positive aspect." + "reason": "The score is 0.50 because the analysis request lacked an input parameter for the first tool call, which limited the ability to fully assess the connectivity patterns as intended." }, { "name": "Deterministic Argument Correctness", @@ -534,7 +528,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.263683" + "created_at": "2025-12-09 11:28:17.258301" } }, "cortex_morphologies": { @@ -559,52 +553,60 @@ "entitycore" ] }, - "ai_response": "I will query the EntityCore cell morphology catalog to return the first 5 morphologies so you can review cortex-related reconstructions; this uses the \\get all\\ endpoint with page_size=5.\\n\\nNow I'll retrieve 5 cell morphologies.## Retrieved 5 cortex morphologies (summary)\\n\\nThese five morphologies were returned by the EntityCore cell morphology catalog (page 1, page_size 5). With these results, the next step will be to fetch full details for any specific morphology you want inspected (assets, measurements, or to download files).\\n\\n**Total items in catalog:** **6108**\\n\\n| # | **Name** | **ID** | **M-type** | **Brain region** | **Created** | **Coordinates** | **Link** |\\n|---:|----------|--------|------------|------------------|-------------:|-----------------|----------|\\n| 1 | **dend-jy180118_B_idB_axon-05311-05329-X10166-Y11730_final_-_Scale_x1.000_y1.025_z1.000_-_Clone_9** | **7e69531a-c976-4b4c-be44-881e5e3defdd** | **L23_PC** (Layer 2/3 Pyramidal Cell) | **Frontal pole, layer 1 (FRP1)** | 2025-11-12T13:23:17.324607Z | **(x: 0.0, y: 0.0, z: 0.0)** | [View details](https://openbraininstitute.org/app/entity/7e69531a-c976-4b4c-be44-881e5e3defdd) |\\n| 2 | **dend-jy180118_B_idB_axon-05311-05329-X10166-Y11730_final_-_Scale_x1.000_y1.025_z1.000_-_Clone_9** | **9f4c2eda-1d41-4d8b-88fe-f8db27efb3b7** | **L23_PC** | **Frontal pole, layer 1 (FRP1)** | 2025-11-12T13:19:57.974966Z | **(x: 0.0, y: 0.0, z: 0.0)** | [View details](https://openbraininstitute.org/app/entity/9f4c2eda-1d41-4d8b-88fe-f8db27efb3b7) |\\n| 3 | **dend-jy180118_B_idB_axon-05311-05329-X10166-Y11730_final_-_Scale_x1.000_y1.025_z1.000_-_Clone_9** | **1e3c212b-aa17-4baa-8390-9d229d4c192b** | **L23_PC** | **Frontal pole, layer 1 (FRP1)** | 2025-11-11T19:29:37.112470Z | **(x: 0.0, y: 0.0, z: 0.0)** | [View details](https://openbraininstitute.org/app/entity/1e3c212b-aa17-4baa-8390-9d229d4c192b) |\\n| 4 | **dend-jy180118_B_idB_axon-05311-05329-X10166-Y11730_final_-_Scale_x1.000_y1.025_z1.000_-_Clone_9** | **cb705953-a653-4e3d-8d04-b2b2c53dff2a** | **L23_PC** | **Frontal pole, layer 1 (FRP1)** | 2025-11-11T19:05:43.471593Z | **(x: 0.0, y: 0.0, z: 0.0)** | [View details](https://openbraininstitute.org/app/entity/cb705953-a653-4e3d-8d04-b2b2c53dff2a) |\\n| 5 | **dend-jy180118_B_idB_axon-05311-05329-X10166-Y11730_final_-_Scale_x1.000_y1.025_z1.000_-_Clone_9** | **1d4c8bc1-099e-4151-a886-1c7d4ae27f30** | **L23_PC** | **Frontal pole, layer 1 (FRP1)** | 2025-11-11T15:31:35.149068Z | **(x: 0.0, y: 0.0, z: 0.0)** | [View details](https://openbraininstitute.org/app/entity/1d4c8bc1-099e-4151-a886-1c7d4ae27f30) |\\n\\n### Notes & available assets (per morphology)\\n- Each entry includes multiple asset files (examples present in the records: .swc, .h5, .asc). \\n- Licenses: **CC BY-NC 4.0** for these entries. \\n- Subject species: **Mus musculus** (Generic).\\n\\nIf you want more detail for any of these (full asset list and download IDs, measurements, mtype metadata, or to fetch more items/pages), indicate which ID(s) to inspect and the next step will be to retrieve the full morphology record(s).", + "ai_response": "## Summary \u2014 5 cortical morphologies (first page)\n\nTotal morphologies found within **Cerebral cortex (CTX)**: **4856** \n(Displayed: page 1, 5 items per page)\n\nBelow are the 5 cell morphologies returned (each entry includes key metadata and the entity link).\n\n### 1) **test111** \n- **ID:** **8e879746-712e-48f0-a3b7-ecd69bbbfce8** \n- **Name:** **test111** \n- **Description:** Uploaded morphology for analysis \n- **Created / Updated:** 2025-11-29T08:19:47.674790Z / 2025-11-29T08:19:47.674790Z \n- **Author:** **Daniel Keller** \n- **Brain region:** **Frontal pole, layer 1 (FRP1)** \u2014 annotation_value **68** \n- **Subject species:** *Mus musculus* \n- **Assets:** 3 files (swc, h5, asc) \u2014 example asset ID: **0752d010-a6ef-4fd0-9bc7-e749050c8e86** (swc) \n- **Cell morphology protocol:** **Reconstruction from Christiaan de Kock with 350um slicing thickness** (digital_reconstruction) \n- **Access:** public (authorized_public: true) \n- **EntityCore link:** [View Details](https://staging.openbraininstitute.org/app/entity/8e879746-712e-48f0-a3b7-ecd69bbbfce8)\n\n---\n\n### 2) **My Morphology Analysis** \n- **ID:** **cbf7ac43-c460-4e94-a830-7d9bfc9474a2** \n- **Name:** **My Morphology Analysis** \n- **Description:** Uploaded morphology for analysis \n- **Created / Updated:** 2025-11-29T08:10:40.730440Z / 2025-11-29T08:10:40.730440Z \n- **Author:** **Daniel Keller** \n- **Brain region:** **Frontal pole, layer 1 (FRP1)** \u2014 annotation_value **68** \n- **Subject species:** *Mus musculus* \n- **Assets:** 3 files (asc, h5, swc) \u2014 example asset ID: **bad6e85b-73df-462d-9721-8fc13f426678** (asc) \n- **Access:** public \n- **EntityCore link:** [View Details](https://staging.openbraininstitute.org/app/entity/cbf7ac43-c460-4e94-a830-7d9bfc9474a2)\n\n---\n\n### 3) **My Morphology Analysis** \n- **ID:** **cee0e657-4165-4371-845b-e1be26effc93** \n- **Name:** **My Morphology Analysis** \n- **Description:** Uploaded morphology for analysis \n- **Created / Updated:** 2025-11-29T08:08:47.490369Z / 2025-11-29T08:08:47.490369Z \n- **Author:** **Daniel Keller** \n- **Brain region:** **Frontal pole, layer 1 (FRP1)** \u2014 annotation_value **68** \n- **Subject species:** *Mus musculus* \n- **Assets:** 3 files (swc, h5, asc) \u2014 example asset ID: **64e078af-3d6c-4892-9fc7-01301296717d** (swc) \n- **Access:** public \n- **EntityCore link:** [View Details](https://staging.openbraininstitute.org/app/entity/cee0e657-4165-4371-845b-e1be26effc93)\n\n---\n\n### 4) **My Morphology Analysis** \n- **ID:** **d598afcb-d8eb-4765-a336-2ac107b6d03a** \n- **Name:** **My Morphology Analysis** \n- **Description:** Uploaded morphology for analysis \n- **Created / Updated:** 2025-11-29T07:55:41.075098Z / 2025-11-29T07:55:41.075098Z \n- **Author:** **Daniel Keller** \n- **Brain region:** **Frontal pole, layer 1 (FRP1)** \u2014 annotation_value **68** \n- **Subject species:** *Mus musculus* \n- **Assets:** multiple (large swc, asc, h5) \u2014 example asset ID: **5535ec1f-f714-45c0-bf03-bdbe47772acb** (swc) \n- **Access:** public \n- **EntityCore link:** [View Details](https://staging.openbraininstitute.org/app/entity/d598afcb-d8eb-4765-a336-2ac107b6d03a)\n\n---\n\n### 5) **My Morphology Analysis** \n- **ID:** **94356d2f-096b-4366-9372-ff8dc04500a5** \n- **Name:** **My Morphology Analysis** \n- **Description:** Uploaded morphology for analysis \n- **Created / Updated:** 2025-11-29T07:00:25.757912Z / 2025-11-29T07:00:25.757912Z \n- **Author:** **Daniel Keller** \n- **Brain region:** **Frontal pole, layer 1 (FRP1)** \u2014 annotation_value **68** \n- **Subject species:** *Mus musculus* \n- **Assets:** 3 files (swc, asc, h5) \u2014 example asset ID: **a4a859c0-aab5-4825-80f4-68c1e1c62347** (swc) \n- **Access:** public \n- **EntityCore link:** [View Details](https://staging.openbraininstitute.org/app/entity/94356d2f-096b-4366-9372-ff8dc04500a5)\n\n---\n\nIf you want additional pages (more morphologies) or to filter by specific features (e.g., layer, cell type, mtype, or protocol), tell me which filter to apply and I will retrieve the next results.", "actual_tool_calls": [ + { + "name": "entitycore-brainregion-getall", + "arguments": { + "semantic_search": "cortex" + } + }, { "name": "entitycore-cellmorphology-getall", - "arguments": {} + "arguments": { + "within_brain_region_brain_region_id": "abe63c70-1eb0-4b42-9421-d2c914ecb493" + } } ], "results": { "metrics": [ { "name": "Correctness [GEval]", - "score": 0.4653298006903742, - "success": false, + "score": 0.5362164060624173, + "success": true, "threshold": 0.5, - "reason": "The actual output provides relevant information about cortex morphologies and includes a summary of the retrieved data, which aligns with the input request. However, it lacks the structured format and specific details outlined in the expected output, such as descriptions, contributors, and licenses for each morphology. While it does mention the total items in the catalog and provides links, it does not fully meet the completeness and appropriateness criteria of the expected output." + "reason": "The actual output provides relevant information about cortical morphologies, aligning with the input request. However, it lacks the expected structure and specific details such as morphological type, contributors, and license information, which are crucial for full alignment with the expected output. While it does present five morphologies, the format and missing sections prevent it from achieving a higher score." }, { "name": "Tool Correctness", - "score": 0.5, + "score": 1.0, "success": true, "threshold": 0.5, - "reason": "Incorrect tool usage: missing tools ['entitycore-brainregion-getall'] and out-of-order tools ['entitycore-brainregion-getall']; expected ['entitycore-brainregion-getall', 'entitycore-cellmorphology-getall'], called ['entitycore-cellmorphology-getall']. See more details above." + "reason": "Correct ordering: all expected tools ['entitycore-brainregion-getall', 'entitycore-cellmorphology-getall'] were called in the correct order." }, { "name": "Argument Correctness", "score": 0.0, "success": false, "threshold": 0.5, - "reason": "The score is 0.00 because there was no input parameter provided, which is essential for the tool to function correctly." + "reason": "The score is 0.00 because the tool calls failed to specify the number of cortex morphologies to retrieve and did not address the request to retrieve 5 cortex morphologies. This lack of clarity and adherence to the input requirements resulted in a low score." }, { "name": "Deterministic Argument Correctness", - "score": 0.0, - "success": false, + "score": 1.0, + "success": true, "threshold": 0.5, - "reason": "Incomplete tool usage: missing tools [ToolCall(\n name=\"entitycore-brainregion-getall\",\n input_parameters={\n \"semantic_search\": \"cortex\"\n }\n), ToolCall(\n name=\"entitycore-cellmorphology-getall\",\n input_parameters={\n \"within_brain_region_brain_region_id\": \"abe63c70-1eb0-4b42-9421-d2c914ecb493\"\n }\n)]; expected ['entitycore-brainregion-getall', 'entitycore-cellmorphology-getall'], called ['entitycore-cellmorphology-getall']. See more details above." + "reason": "All expected tools ['entitycore-brainregion-getall', 'entitycore-cellmorphology-getall'] were called (order not considered)." }, { "name": "Overall Argument Correctness", - "score": 0.0, - "success": false, + "score": 1.0, + "success": true, "threshold": 0.5, "reason": "" } ], - "created_at": "2025-11-28 16:33:30.263716" + "created_at": "2025-12-09 11:28:17.258319" } }, "get_specific_circuit": { @@ -624,10 +626,10 @@ "circuit" ] }, - "ai_response": "To inspect the circuit structure (node/edge populations and available filterable properties) I will retrieve the circuit metrics with moderate detail for nodes and edges so we can see node property names, unique values, and edge populations needed for further connectivity analyses. With that information I can provide full circuit details and links.## Overview & next step reasoning\\n\\nYou asked for details about circuit **2eb602ea-9ffc-42a2-9fb7-5234921343d0**. To characterize its structure I retrieved the circuit metrics with moderate node/edge detail so we can see the named node populations, available node sets (for filtering), and the listed edge populations. With these names we can run specific connectivity analyses (e.g., connection probabilities or synapse counts) next if you want \\u2014 tell me which edge population and which pre/post neuron subsets to analyze.\\n\\n---\\n\\n## Circuit ID\\n- **ID:** **2eb602ea-9ffc-42a2-9fb7-5234921343d0**\\n\\n---\\n\\n## Node populations (summary)\\n- **Number of biophysical node populations:** **1**\\n - **Names:** `S1nonbarrel_neurons`\\n- **Number of virtual node populations:** **3**\\n - **Names:** `POm`, `VPM`, `external_S1nonbarrel_neurons`\\n- **Biophysical node population details:** returned as `null` in this response (no further per-population object included in this metrics output)\\n- **Virtual node population details:** returned as `null` for each in this response\\n\\nNote: the metrics response included counts and names but did not return expanded objects for the node populations at this level of detail.\\n\\n---\\n\\n## Predefined node sets (names_of_nodesets)\\n- The circuit provides many predefined node sets useful for filtering/grouping. Selected examples (full list included below):\\n - `Mosaic`, `All`, `Excitatory`, `Inhibitory`\\n - Layer-specific sets: `Layer1`, `Layer2`, `Layer3`, `Layer4`, `Layer5`, `Layer6`\\n - Layer + excitatory/inhibitory aggregates: `Layer2Excitatory`, `Layer3Excitatory`, `Layer23Excitatory`, `Layer4Excitatory`, `Layer5Excitatory`, `Layer6Excitatory`, `Layer1Inhibitory`, `Layer23Inhibitory`, `Layer4Inhibitory`, `Layer5Inhibitory`, `Layer6Inhibitory`\\n - Cell types / mtypes: `L1_DAC`, `L1_HAC`, `L1_LAC`, `L1_NGC-DA`, `L1_NGC-SA`, `L1_SAC`, `L23_BP`, `L23_BTC`, `L23_CHC`, `L23_DBC`, `L23_LBC`, `L23_MC`, `L23_NBC`, `L23_NGC`, `L23_SBC`, `L2_IPC`, `L2_TPC:A`, `L2_TPC:B`, `L3_TPC:A`, `L3_TPC:C`, `L4_BP`, `L4_BTC`, `L4_CHC`, `L4_DBC`, `L4_LBC`, `L4_MC`, `L4_NBC`, `L4_NGC`, `L4_SBC`, `L4_SSC`, `L4_TPC`, `L4_UPC`, `L5_BP`, `L5_BTC`, `L5_CHC`, `L5_DBC`, `L5_LBC`, `L5_MC`, `L5_NBC`, `L5_NGC`, `L5_SBC`, `L5_TPC:A`, `L5_TPC:B`, `L5_TPC:C`, `L5_UPC`, `L6_BP`, `L6_BPC`, `L6_BTC`, `L6_CHC`, `L6_DBC`, `L6_HPC`, `L6_IPC`, `L6_LBC`, `L6_MC`, `L6_NBC`, `L6_NGC`, `L6_SBC`, `L6_TPC:A`, `L6_TPC:C`, `L6_UPC`\\n - Firing-type groups: `bAC`, `bIR`, `bNAC`, `bSTUT`, `cACint`, `cADpyr`, `cIR`, `cNAC`, `cSTUT`, `dNAC`, `dSTUT`\\n - Hex/grid and parcel groups: `hex0`..`hex6`, `hex_O1`, `hex_O1PV`, `hex_O1Sst`, `hex_O15HT3aR`, `nbS1-HEX0`..`nbS1-HEX6`, and layer-specific hex subsets like `nbS1-HEX0-L1`, etc.\\n - Thalamocortical projection sets: `proj_Thalamocortical_VPM_Source`, `proj_Thalamocortical_POM_Source`\\n- Full returned node set list (in original order):\\n - Mosaic, All, Excitatory, Inhibitory, L1_DAC, L1_HAC, L1_LAC, L1_NGC-DA, L1_NGC-SA, L1_SAC, L23_BP, L23_BTC, L23_CHC, L23_DBC, L23_LBC, L23_MC, L23_NBC, L23_NGC, L23_SBC, L2_IPC, L2_TPC:A, L2_TPC:B, L3_TPC:A, L3_TPC:C, L4_BP, L4_BTC, L4_CHC, L4_DBC, L4_LBC, L4_MC, L4_NBC, L4_NGC, L4_SBC, L4_SSC, L4_TPC, L4_UPC, L5_BP, L5_BTC, L5_CHC, L5_DBC, L5_LBC, L5_MC, L5_NBC, L5_NGC, L5_SBC, L5_TPC:A, L5_TPC:B, L5_TPC:C, L5_UPC, L6_BP, L6_BPC, L6_BTC, L6_CHC, L6_DBC, L6_HPC, L6_IPC, L6_LBC, L6_MC, L6_NBC, L6_NGC, L6_SBC, L6_TPC:A, L6_TPC:C, L6_UPC, bAC, bIR, bNAC, bSTUT, cACint, cADpyr, cIR, cNAC, cSTUT, dNAC, dSTUT, Layer1, Layer2, Layer3, Layer4, Layer5, Layer6, Layer2Excitatory, Layer3Excitatory, Layer23Excitatory, Layer4Excitatory, Layer5Excitatory, Layer6Excitatory, Layer1Inhibitory, Layer23Inhibitory, Layer4Inhibitory, Layer5Inhibitory, Layer6Inhibitory, hex0, hex1, hex2, hex3, hex4, hex5, hex6, hex_O1, hex_O1PV, hex_O1Sst, hex_O15HT3aR, proj_Thalamocortical_VPM_Source, proj_Thalamocortical_POM_Source, nbS1-HEX0, nbS1-HEX1, nbS1-HEX2, nbS1-HEX3, nbS1-HEX4, nbS1-HEX5, nbS1-HEX6, nbS1-HEX0-L1, nbS1-HEX1-L1, nbS1-HEX2-L1, nbS1-HEX3-L1, nbS1-HEX4-L1, nbS1-HEX5-L1, nbS1-HEX6-L1, nbS1-HEX0-L23, nbS1-HEX1-L23, nbS1-HEX2-L23, nbS1-HEX3-L23, nbS1-HEX4-L23, nbS1-HEX5-L23, nbS1-HEX6-L23, nbS1-HEX0-L4, nbS1-HEX1-L4, nbS1-HEX2-L4, nbS1-HEX3-L4, nbS1-HEX4-L4, nbS1-HEX5-L4, nbS1-HEX6-L4, nbS1-HEX0-L5, nbS1-HEX1-L5, nbS1-HEX2-L5, nbS1-HEX3-L5, nbS1-HEX4-L5, nbS1-HEX5-L5, nbS1-HEX6-L5, nbS1-HEX0-L6, nbS1-HEX1-L6, nbS1-HEX2-L6, nbS1-HEX3-L6, nbS1-HEX4-L6, nbS1-HEX5-L6, nbS1-HEX6-L6\\n\\n---\\n\\n## Edge populations (synaptic connections)\\n- **Number of chemical edge populations:** **4**\\n- **Names of chemical edge populations:**\\n - `S1nonbarrel_neurons__S1nonbarrel_neurons__chemical`\\n - `POm__S1nonbarrel_neurons__chemical`\\n - `VPM__S1nonbarrel_neurons__chemical`\\n - `external_S1nonbarrel_neurons__S1nonbarrel_neurons__chemical`\\n- **Number of electrical edge populations:** **0**\\n- **Chemical edge population details:** returned as `null` for each in this response (no expanded edge objects included at this level)\\n\\nThese names indicate:\\n- Intracircuit recurrent chemical connectivity among S1 neurons\\n- Thalamocortical inputs from POm and VPM to S1 neurons\\n- External (virtual) inputs to the S1 neuronal population\\n\\n---\\n\\n## What the current response did and what we can do next\\n- The circuit metrics response provided the canonical names of node populations, many nodesets usable for filtering/grouping, and the list of chemical edge populations available for connectivity analysis.\\n- The returned objects for biophysical/virtual populations and edge population details were `null` in this output. If you want per-population property tables (e.g., property_names, property_unique_values, property_value_counts) or detailed edge statistics, we can:\\n - Run a deeper metrics request with higher node/edge detail (increase level_of_detail_nodes and/or level_of_detail_edges), or\\n - Use the connectivity metrics tool to compute connection probabilities and mean synapses for a chosen edge population and specific pre/post node sets or property filters.\\n\\nGiven that we now have the edge population names and nodeset names, the next logical action is to compute connectivity metrics for whichever connection you care about (for example: local S1->S1 chemical connectivity grouped by mtype or layer, or thalamus VPM->S1). Tell me which edge population and how you'd like to filter/group (e.g., pre_node_set = \\Excitatory\\\", post_node_set = \\Layer4\\\", group_by = \\mtype\\\"), and I will run the connectivity analysis.", + "ai_response": "## Circuit details \u2014 **ID: 2eb602ea-9ffc-42a2-9fb7-5234921343d0**\n\n### Summary\n- **Name:** nbS1-O1-vSub-nCN-HEX0-L2-01 \n- **Type:** circuit (scale: **small**) \n- **Build category:** **computational_model** \n- **Authorized public:** **true** \n- **URL (EntityCore):** [View circuit details](https://staging.openbraininstitute.org/app/entity/2eb602ea-9ffc-42a2-9fb7-5234921343d0)\n\n---\n\n### Key metadata\n| Field | Value |\n|---|---|\n| **ID** | 2eb602ea-9ffc-42a2-9fb7-5234921343d0 |\n| **Description** | A volumetric subcircuit (vSub) extracted from the nbS1-O1 circuit, around a neuron with high centrality in the network\u2019s connectivity graph, located in layer 2 of subcolumn HEX0. The subcircuit consists of the central neuron and its nine nearest neighbors, capturing the local connectivity within the parent circuit. The suffix \u201c01\u201d denotes the first instance of such a subnetwork. |\n| **Brain region** | **Primary somatosensory area (SSp)** \u2014 ID: **61d89b07-dfa0-439a-9187-7ebfe60e212b** |\n| **Subject** | **Average rat P14** \u2014 species: *Rattus norvegicus* (ID: 3b1c2a25-b4fb-468d-98d2-d2d431ac8b4a) |\n| **Experiment date** | 2024-11-01T00:00:00Z |\n| **Contact email** | michael.reimann@openbraininstitute.org |\n| **Published in** | Reimann et al and Isbister et al |\n| **License** | CC BY 4.0 \u2014 https://creativecommons.org/licenses/by/4.0/ |\n| **Has morphologies** | **true** |\n| **Has point neurons** | false |\n| **Has electrical cell models** | **true** |\n| **Has spines** | false |\n| **Number neurons** | 10 |\n| **Number synapses** | 186 |\n| **Number connections** | 15 |\n| **Root circuit ID** | 0f25396a-1c16-43d7-9943-43e78f84e1ed |\n| **Created / Updated** | 2025-07-01T09:02:39.094768Z |\n\n---\n\n### Contributors (selection)\nA large set of contributors and organizations are recorded. Example contributors (pref_label):\n- Alexis Arnaudon\n- Omar Awile\n- Natali Barros-Zulaica\n- Jorge Blanco Alonso\n- Elvis Boci\n- Sirio Bola\u00f1os-Puchet\n- Giuseppe Chindemi\n- Beno\u00eet Coste\n- Jean-Denis Courcol\n- Tanguy Damart\n- ... (many more; full list available in the record)\n\n---\n\n### Assets (available labels, storage IDs)\n- **sonata_circuit** (directory) \u2014 storage id: **a6cec94a-0013-475e-b5ab-89d7cdfb5e92** \n- **simulation_designer_image** \u2014 main.png \u2014 storage id: **1dce066a-679d-4228-a25a-711c4694060a** \n- **compressed_sonata_circuit** \u2014 circuit.gz \u2014 storage id: **736b961f-ef7a-4441-81d1-56c4f82bedab** \n- **node_stats** \u2014 node_stats.webp \u2014 storage id: **e9098491-a598-4c47-87bf-f803fae4d362** \n- **circuit_connectivity_matrices** (directory) \u2014 storage id: **22678058-e968-430e-8d5e-8d090272e025** \n- **circuit_visualization** \u2014 circuit_visualization.webp \u2014 storage id: **91dd7f45-3af4-4f54-b27d-4214cffa16bf** \n- **network_stats_a** \u2014 network_stats_a.webp \u2014 storage id: **f791535d-24e5-49a5-b42b-fb7b80c3ae69** \n- **network_stats_b** \u2014 network_stats_b.webp \u2014 storage id: **f2b6b574-9a0c-4afa-88fa-c6855b33cd43**\n\n(Note: asset entries include path and storage_type \"aws_s3_internal\". The EntityCore record provides the full_path for each asset.)\n\n---\n\nIf you want any of the following next steps, I can proceed:\n- Retrieve detailed circuit structure (node/edge populations and available filterable properties) using the circuit metrics endpoint (obione-circuitmetrics-getone with level_of_detail_nodes=1). \n- Analyze connectivity patterns (connection probabilities, mean synapse counts) for specific pre/post populations using obione-circuitconnectivitymetrics-getone (after we inspect available edge populations). \n- Download a specific asset (tell me which asset label or storage id you want).", "actual_tool_calls": [ { - "name": "obione-circuitmetrics-getone", + "name": "entitycore-circuit-getone", "arguments": { "circuit_id": "2eb602ea-9ffc-42a2-9fb7-5234921343d0" } @@ -637,31 +639,31 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.4548528662574201, - "success": false, + "score": 0.8622402310750299, + "success": true, "threshold": 0.5, - "reason": "The actual output provides some relevant information about the circuit ID, including node and edge populations, but it lacks key details present in the expected output, such as core identifiers, summary/description, and specific counts and capabilities. While it attempts to address the request, it does not follow the expected structure closely and misses coherence in presenting the overall circuit details." + "reason": "The actual output closely matches the expected output in structure and content, providing detailed circuit information relevant to the input query about circuit ID. It includes key identifiers, a summary, and metadata that align well with the expected details. However, it slightly deviates in the formatting of sections and the URL provided, which could be improved to match the expected output more precisely." }, { "name": "Tool Correctness", - "score": 0.0, - "success": false, + "score": 1.0, + "success": true, "threshold": 0.5, - "reason": "Incorrect tool usage: missing tools ['entitycore-circuit-getone'] and out-of-order tools ['entitycore-circuit-getone']; expected ['entitycore-circuit-getone'], called ['obione-circuitmetrics-getone']. See more details above." + "reason": "Correct ordering: all expected tools ['entitycore-circuit-getone'] were called in the correct order." }, { "name": "Argument Correctness", "score": 1.0, "success": true, "threshold": 0.5, - "reason": "The score is 1.00 because there were no incorrect tool calls, indicating that the input was handled perfectly." + "reason": "The score is 1.00 because there were no incorrect tool calls and the input was clear and specific." }, { "name": "Deterministic Argument Correctness", - "score": 0.0, - "success": false, + "score": 1.0, + "success": true, "threshold": 0.5, - "reason": "Incomplete tool usage: missing tools [ToolCall(\n name=\"entitycore-circuit-getone\",\n input_parameters={\n \"circuit_id\": \"2eb602ea-9ffc-42a2-9fb7-5234921343d0\"\n }\n)]; expected ['entitycore-circuit-getone'], called ['obione-circuitmetrics-getone']. See more details above." + "reason": "All expected tools ['entitycore-circuit-getone'] were called (order not considered)." }, { "name": "Overall Argument Correctness", @@ -671,7 +673,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.263746" + "created_at": "2025-12-09 11:28:17.258336" } }, "species_list": { @@ -690,7 +692,7 @@ "entitycore" ] }, - "ai_response": "I will query the EntityCore species registry to retrieve the first 10 species entries (this uses the platform's \\get all species\\ endpoint and will return metadata including total count). Now I'll request page 1 with page_size 10.## Retrieved species (requested: first 10)\\n\\nI requested page 1 with page_size 10 from the EntityCore species registry to return the first available entries. The API returned 9 total items, so below are the first (and only) 9 species available.\\n\\n| # | **Species Name** | **ID** | **Taxonomy ID** | **Creation Date** |\\n|---:|------------------|--------|------------------|-------------------|\\n| 1 | **Aquarana catesbeiana** | **d322a735-3ead-4efe-840e-2b4fda9c025a** | **NCBITaxon:8400** | 2025-06-27T11:10:28.715311Z |\\n| 2 | **Cricetulus griseus** | **9779ac1f-45e9-4d32-8c85-5fdfa7251af0** | **NCBITaxon:10029** | 2025-06-27T11:10:28.715307Z |\\n| 3 | **Felis catus** | **ea748db9-e020-407a-aeb6-db2c3a038a70** | **NCBITaxon:9685** | 2025-06-27T11:10:28.715308Z |\\n| 4 | **Homo sapiens** | **ff3fe32a-30ff-44ee-90a7-bd04e85bfcdd** | **NCBITaxon:9606** | 2025-06-27T11:10:28.715304Z |\\n| 5 | **Hybrid human-mouse** | **0f756132-e16d-4e72-b18e-859835e1f291** | **NA** | 2025-06-27T11:10:28.715312Z |\\n| 6 | **Loligo pealeii** | **22f1a831-85b9-4f5f-811e-60de1b343f52** | **NCBITaxon:6619** | 2025-06-27T11:10:28.715310Z |\\n| 7 | **Mus musculus** | **b7ad4cca-4ac2-4095-9781-37fb68fe9ca1** | **NCBITaxon:10090** | 2025-06-27T11:10:53.183929Z |\\n| 8 | **Rattus norvegicus** | **3b1c2a25-b4fb-468d-98d2-d2d431ac8b4a** | **NCBITaxon:10116** | 2025-06-27T11:11:57.148444Z |\\n| 9 | **Xenopus laevis** | **930cdbd3-0f0a-42a8-9b2d-19a2beaa14a7** | **NCBITaxon:8355** | 2025-06-27T11:10:28.715306Z |\\n\\n- Total items returned by the registry: **9** (so there are fewer than 10 species available in the database).\\n\\nNext logical step: if you want additional metadata (e.g., strains, species details) I can fetch the detailed record for any of these species by using its **ID**, or run another paginated query if you suspect more items exist. Which would you like me to do?", + "ai_response": "## Species \u2014 first page (10 requested)\n\nThe query returned the available species entries (sorted by name). There are **9 total items** in this page.\n\n### Summary\n- **Total items available:** **9**\n- **Page:** 1 of 1 (requested page_size = 10)\n\n### Species list (first 9)\n\n| # | **ID** | **Name** | **Taxonomy ID** | **Creation Date** | **Update Date** |\n|---:|--------|----------|-----------------|-------------------:|-----------------:|\n| 1 | **d322a735-3ead-4efe-840e-2b4fda9c025a** | *Aquarana catesbeiana* | **NCBITaxon:8400** | 2025-06-27T11:10:28.715311Z | 2025-06-27T11:10:28.715312Z |\n| 2 | **9779ac1f-45e9-4d32-8c85-5fdfa7251af0** | *Cricetulus griseus* | **NCBITaxon:10029** | 2025-06-27T11:10:28.715307Z | 2025-06-27T11:10:28.715308Z |\n| 3 | **ea748db9-e020-407a-aeb6-db2c3a038a70** | *Felis catus* | **NCBITaxon:9685** | 2025-06-27T11:10:28.715308Z | 2025-06-27T11:10:28.715309Z |\n| 4 | **ff3fe32a-30ff-44ee-90a7-bd04e85bfcdd** | *Homo sapiens* | **NCBITaxon:9606** | 2025-06-27T11:10:28.715304Z | 2025-06-27T11:10:28.715305Z |\n| 5 | **0f756132-e16d-4e72-b18e-859835e1f291** | *Hybrid human-mouse* | **NA** | 2025-06-27T11:10:28.715312Z | 2025-06-27T11:10:28.715313Z |\n| 6 | **22f1a831-85b9-4f5f-811e-60de1b343f52** | *Loligo pealeii* | **NCBITaxon:6619** | 2025-06-27T11:10:28.715310Z | 2025-06-27T11:10:28.715310Z |\n| 7 | **b7ad4cca-4ac2-4095-9781-37fb68fe9ca1** | *Mus musculus* | **NCBITaxon:10090** | 2025-06-27T11:10:53.183929Z | 2025-06-27T11:10:53.183929Z |\n| 8 | **3b1c2a25-b4fb-468d-98d2-d2d431ac8b4a** | *Rattus norvegicus* | **NCBITaxon:10116** | 2025-06-27T11:11:57.148444Z | 2025-06-27T11:11:57.148444Z |\n| 9 | **930cdbd3-0f0a-42a8-9b2d-19a2beaa14a7** | *Xenopus laevis* | **NCBITaxon:8355** | 2025-06-27T11:10:28.715306Z | 2025-06-27T11:10:28.715306Z |\n\n---\n\nWould you like any of these species expanded into full details (by providing the species ID to retrieve), or would you like me to search/filter by a specific name or taxonomy term?", "actual_tool_calls": [ { "name": "entitycore-species-getall", @@ -703,10 +705,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.6400358146726484, + "score": 0.6566399827580228, "success": true, "threshold": 0.5, - "reason": "The actual output is relevant to the input, as it addresses the request for the first 10 available species and provides a list of 9 species retrieved from the EntityCore species registry. However, the structure differs significantly from the expected output, which presents species names and taxonomy IDs in a more concise format without the additional API query details. While the content is accurate and relevant, the overall presentation lacks the clarity and simplicity of the expected output." + "reason": "The actual output provides a structured list of species, which aligns with the request for the first 10 available species. However, it only includes 9 species, which does not fully meet the expectation of providing 10. The output is coherent and relevant to the topic, but it lacks the completeness of the expected output, which presents species in a more narrative format. Additionally, the actual output does not explicitly mention the taxonomy IDs in a clear manner as seen in the expected output." }, { "name": "Tool Correctness", @@ -737,7 +739,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.263777" + "created_at": "2025-12-09 11:28:17.258351" } }, "cerebellum_morphologies": { @@ -762,7 +764,7 @@ "entitycore" ] }, - "ai_response": "I'll first resolve the brain region name to its internal ID so I can reliably query morphologies within the cerebellum. Next I'll use that brain-region ID to fetch cell morphologies located in the cerebellum.I will use the primary \\Cerebellum\\ region ID to fetch cell morphologies within that region.The first page returned 3 morphologies; to reach five items I'll fetch the next page of cerebellum morphologies to collect two more entries.## Cerebellum \\u2014 5 cell morphologies\\n\\nGiven that the Cerebellum region ID is resolved, below are five morphologies found within **Cerebellum (ID: 062ebc92-cebb-498a-a76d-b5ef1baf02cf)**. Links point to each entity's detail page.\\n\\n### 1. **17302_00064** (ID: 12811d3a-9705-4eec-867c-65053d646d5b)\\n- **Type:** cell_morphology \\n- **Brain region shown in record:** root \\n- **Subject species:** *Mus musculus* \\n- **M-type:** **Interneuron** \\n- **Location coordinates:** **(x: 7242.70068359375, y: 2798.824951171875, z: 3697.524658203125)** \\n- **Description:** Neuronal morphology of cell 17302_00064 in root, reconstructed by SEU and curated by BBP; contains whole brain axonal reconstruction. \\n- **Assets:** SWC, ASC, H5 files (e.g., 17302_00064.swc) \\n- **License:** CC BY 4.0 \\n- **Link:** [View 17302_00064](https://openbraininstitute.org/app/entity/12811d3a-9705-4eec-867c-65053d646d5b)\\n\\n### 2. **soma_10c** (ID: e892fa95-c724-457a-8dc6-176f5d6cc5d9)\\n- **Type:** cell_morphology \\n- **Brain region:** **Cerebellum** \\n- **Subject species:** *Mus musculus* \\n- **M-type:** **CBXpu_PuC** (Cerebellum Purkinje Cell) \\n- **Description:** Purkinje cell located in Purkinje cell layer; flask-like soma (~7.5 \\u00b5m radius), large planar dendritic tree in molecular layer, single long axon to white matter. \\n- **Assets:** soma_10c.swc, soma_10c.h5, soma_10c.asc \\n- **License:** CC BY-NC-SA 4.0 \\n- **Link:** [View soma_10c](https://openbraininstitute.org/app/entity/e892fa95-c724-457a-8dc6-176f5d6cc5d9)\\n\\n### 3. **01bc** (ID: d083a0bd-00a4-4594-b52f-950a5dd3ec70)\\n- **Type:** cell_morphology \\n- **Brain region:** **Cerebellum** \\n- **Subject species:** *Mus musculus* \\n- **M-type:** **CBXmo_BC** (Cerebellum Basket Cell) \\n- **Description:** Soma pyramidal/oval (~6 \\u00b5m radius) in inner molecular layer; long upward dendrites, axon parallel to Purkinje layer with descending and ascending collaterals. \\n- **Assets:** 01bc.swc, 01bc.h5, 01bc.asc \\n- **License:** CC BY-NC-SA 4.0 \\n- **Link:** [View 01bc](https://openbraininstitute.org/app/entity/d083a0bd-00a4-4594-b52f-950a5dd3ec70)\\n\\n### 4. **stellate** (ID: 00ba95d3-13e4-4703-ad85-3df816febf53)\\n- **Type:** cell_morphology \\n- **Brain region:** **Cerebellum** \\n- **Subject species:** *Mus musculus* \\n- **M-type:** **CBXmo_StC** (Cerebellum Stellate Cell) \\n- **Description:** Small fusiform soma (~4 \\u00b5m radius) in outer molecular layer; long thin contorted aspinous dendrites branching in all directions; axon branches immediately into short collaterals. \\n- **Assets:** stellate.swc, stellate.h5, stellate.asc \\n- **License:** CC BY-NC-SA 4.0 \\n- **Link:** [View stellate](https://openbraininstitute.org/app/entity/00ba95d3-13e4-4703-ad85-3df816febf53)\\n\\n### 5. **GrC2018** (ID: b03ceac9-d657-488c-a43c-01be275d0861)\\n- **Type:** cell_morphology \\n- **Brain region:** **Cerebellum** \\n- **Subject species:** *Mus musculus* \\n- **M-type:** **CBXgr_GrC** (Cerebellum Granule Cell) \\n- **Description:** Very small round soma (~2.5 \\u00b5m radius) in granular layer; 4\\u20135 dendrites ending in claw-like appendages; ascending axon bifurcates into parallel fibers in the molecular layer. \\n- **Assets:** GrC2018.swc, GrC2018.h5, GrC2018.asc \\n- **License:** CC BY-NC-SA 4.0 \\n- **Link:** [View GrC2018](https://openbraininstitute.org/app/entity/b03ceac9-d657-488c-a43c-01be275d0861)\\n\\nSummary: total cerebellum morphologies found by the query = **78**; I returned the first 5 entries across the first two pages. Would you like these as downloadable SWC/H5 links, a CSV of metadata, or more items from the cerebellum?", + "ai_response": "## Cerebellum morphologies \u2014 results\n\n**Query note:** I resolved the brain region *Cerebellum* (ID **062ebc92-cebb-498a-a76d-b5ef1baf02cf**) and requested up to 5 morphologies within that region. The search returned **66 total morphologies**; the first page (requested) returned 3 items. If you want 5 now, I can fetch the next page to reach 5 \u2014 say so and I will retrieve it.\n\n### Summary (page 1)\n- **Total items available:** **66**\n- **Returned on this page:** **3** (shown below)\n\n### Returned morphologies (page 1)\n\n| Name | ID | mtype (label) | Created by | URL |\n|------|----:|---------------|------------|-----|\n| **soma_10c** | **e892fa95-c724-457a-8dc6-176f5d6cc5d9** | **CBXpu_PuC** (Cerebellum Purkinje Cell) | **Aurelien Jaquier** | [View entity](https://staging.openbraininstitute.org/app/entity/e892fa95-c724-457a-8dc6-176f5d6cc5d9) |\n| **01bc** | **d083a0bd-00a4-4594-b52f-950a5dd3ec70** | **CBXmo_BC** (Cerebellum Basket Cell) | **Aurelien Jaquier** | [View entity](https://staging.openbraininstitute.org/app/entity/d083a0bd-00a4-4594-b52f-950a5dd3ec70) |\n| **stellate** | **00ba95d3-13e4-4703-ad85-3df816febf53** | **CBXmo_StC** (Cerebellum Stellate Cell) | **Aurelien Jaquier** | [View entity](https://staging.openbraininstitute.org/app/entity/00ba95d3-13e4-4703-ad85-3df816febf53) |\n\n#### Brief details for each\n\n##### 1) **soma_10c** \u2014 ID: **e892fa95-c724-457a-8dc6-176f5d6cc5d9**\n- *Description:* Purkinje-cell style: flask-like soma (~7.5 \u00b5m radius), elaborate flattened dendritic tree in molecular layer, single long axon to white matter (sole cerebellar output).\n- *Subject species:* *Mus musculus* (Generic Mus musculus)\n- *Assets:* .asc, .h5, .swc (asset IDs listed in entity)\n- *License:* CC BY-NC-SA 4.0\n- *Entity link:* [View entity](https://staging.openbraininstitute.org/app/entity/e892fa95-c724-457a-8dc6-176f5d6cc5d9)\n\n##### 2) **01bc** \u2014 ID: **d083a0bd-00a4-4594-b52f-950a5dd3ec70**\n- *Description:* Pyramidal/oval soma (~6 \u00b5m radius); located inner molecular layer; long dendrites upward; axon runs parallel to Purkinje layer with collaterals.\n- *Subject species:* *Mus musculus*\n- *Assets:* .asc, .swc, .h5\n- *License:* CC BY-NC-SA 4.0\n- *Entity link:* [View entity](https://staging.openbraininstitute.org/app/entity/d083a0bd-00a4-4594-b52f-950a5dd3ec70)\n\n##### 3) **stellate** \u2014 ID: **00ba95d3-13e4-4703-ad85-3df816febf53**\n- *Description:* Small fusiform soma (~4 \u00b5m radius); outer molecular layer; long thin contorted aspinous dendrites; short local axon collaterals.\n- *Subject species:* *Mus musculus*\n- *Assets:* .asc, .swc, .h5\n- *License:* CC BY-NC-SA 4.0\n- *Entity link:* [View entity](https://staging.openbraininstitute.org/app/entity/00ba95d3-13e4-4703-ad85-3df816febf53)\n\n---\n\nWould you like me to fetch the next page so I can show two more (to reach 5), or would you prefer a different sorting/filter (e.g., by mtype or newest)?", "actual_tool_calls": [ { "name": "entitycore-brainregion-getall", @@ -775,23 +777,16 @@ "arguments": { "within_brain_region_brain_region_id": "062ebc92-cebb-498a-a76d-b5ef1baf02cf" } - }, - { - "name": "entitycore-cellmorphology-getall", - "arguments": { - "page": 2, - "within_brain_region_brain_region_id": "062ebc92-cebb-498a-a76d-b5ef1baf02cf" - } } ], "results": { "metrics": [ { "name": "Correctness [GEval]", - "score": 0.7353869349174808, + "score": 0.5378027691066317, "success": true, "threshold": 0.5, - "reason": "The actual output provides detailed information about five cell morphologies from the cerebellum, which is relevant to the input request. It includes specific details such as morphology IDs, types, descriptions, and links, aligning well with the expected content. However, it lacks the explicit mention of contributors, which is a key element in the expected output structure, leading to a slight deduction in the score." + "reason": "The actual output provides relevant information about cerebellum morphologies, addressing the topic of neuroscience. However, it only includes 3 morphologies instead of the requested 5, and it does not follow the expected output structure, which outlines placeholders for 5 morphologies. The response also includes unnecessary prompts for further action, which detracts from the clarity and directness expected in the output." }, { "name": "Tool Correctness", @@ -805,7 +800,7 @@ "score": 0.0, "success": false, "threshold": 0.5, - "reason": "The score is 0.00 because there were no input parameters provided for any of the tool calls to fetch morphologies, which is essential for retrieving the requested information." + "reason": "The score is 0.00 because there were no input parameters provided for the tool call to fetch morphologies from the cerebellum, and no input parameter was specified to indicate the number of morphologies to retrieve. This lack of necessary information resulted in an inability to fulfill the request." }, { "name": "Deterministic Argument Correctness", @@ -822,7 +817,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.263811" + "created_at": "2025-12-09 11:28:17.258368" } }, "off_topic_weather": { @@ -835,16 +830,16 @@ "rejection" ] }, - "ai_response": "I specialize in neuroscience and the Open Brain Platform. I can\\u2019t help with weather or other off-topic topics. If you\\u2019d like, I can help with brain data today\\u2014analyze a circuit, pull up neuron morphology, or run a quick literature/search related to your neuroscience question. What would you like to explore?", + "ai_response": "\u201cI'm specialized in neuroscience and the Open Brain Platform. I can't help with that topic, but I'd be happy to assist with brain research or platform-related questions.\u201d\n\u201cThat's outside my area of expertise. I focus specifically on neuroscience and the Open Brain Platform. Is there anything related to brain data or the platform I can help you with?\u201d", "actual_tool_calls": [], "results": { "metrics": [ { "name": "Correctness [GEval]", - "score": 0.6890006117592387, + "score": 0.5413464829505792, "success": true, "threshold": 0.5, - "reason": "The actual output is relevant to the input in that it clearly states the specialization in neuroscience and the Open Brain Platform, aligning with the expected output's focus. However, while it addresses the off-topic nature of the weather question, it is slightly more verbose and includes additional offers for assistance that were not present in the expected output. This affects the structure and completeness of the response, leading to a lower score despite the relevance." + "reason": "The actual output is relevant to the input in that it maintains a focus on neuroscience and the Open Brain Platform, which aligns with the expected output's intent. However, the structure is not consistent, as the actual output includes two separate responses instead of a single cohesive statement. This affects the coherence and clarity of the response, leading to a moderate score." }, { "name": "Tool Correctness", @@ -875,7 +870,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.263841" + "created_at": "2025-12-09 11:28:17.258384" } }, "morphology_studies": { @@ -896,19 +891,14 @@ "literature_search" ] }, - "ai_response": "I will search recent (last ~5 years) research literature for papers that focus on neuron morphology in the cerebrum to provide representative studies and links; I'll request several results to cover different approaches (reconstructions, morphometrics, and regional comparisons).I'll search the web for recent (\\u22482020\\u20132025) papers on neuron morphology in the cerebrum, prioritizing studies with reconstructions, morphometrics, and regional/cell-type comparisons. I'll retrieve several high-quality results.## Clarification and next step\\n\\nI can gather recent (\\u2248last 5 years) studies on neuron morphology in the cerebrum, including digital reconstructions, morphometric analyses, and regional/cell-type comparisons. To proceed I will perform a targeted literature search and return representative papers with links and short summaries.\\n\\n- Do you want results restricted to:\\n - (A) Human cerebrum only, or\\n - (B) Rodent (mouse/rat) cortex only, or\\n - (C) Any species (mixed) but limited to the last 5 years (2020\\u20132025)?\\n- How many papers would you like (suggest 5\\u201310)?\\n\\nOnce you confirm these preferences I will run the literature search and return selected papers with short summaries and links.\\n\\nReasoning: clarifying species and number of results will let me target the literature search to your needs and avoid returning an overly broad or irrelevant set of papers.", + "ai_response": "## Recent studies on neuron morphology in the cerebrum (selected)\n\nBelow are recent, relevant papers (2022\u20132025) returned from a targeted literature search for \"neuron morphology\" in cerebral regions. Each entry includes citation, date, a short summary from the search results, and the article link. Several results include preview images \u2014 those are embedded below each item.\n\n---\n\n### 1) Neuronal diversity and stereotypy at multiple scales through whole brain morphometry \n- **Author:** Peng, Hanchuan et al. \n- **Published:** 2024-11-26 \n- **Link:** [Neuronal diversity and stereotypy at multiple scales through whole brain morphometry](https://www.nature.com/articles/s41467-024-54745-6) \n- **Summary:** Large-scale whole-brain morphometry in mouse: annotated ~182,497 cell bodies, modeled 15,441 dendritic microenvironments, full morphologies of 1,876 neurons with axonal motifs, and detection of ~2.63 million axonal varicosities. Multi-scale quantification of neuronal diversity and stereotypy across populations and sub-neuronal motifs. \n- **Preview image:** \n ![Fig from Peng et al. 2024 \u2014 whole-brain morphometry](https://media.springernature.com/m685/springer-static/image/art%3A10.1038%2Fs41467-024-54745-6/MediaObjects/41467_2024_54745_Fig1_HTML.png)\n\n---\n\n### 2) An unsupervised map of excitatory neuron dendritic morphology in the mouse visual cortex \n- **Author:** Ecker, Alexander S. et al. \n- **Published:** 2025-04-09 \n- **Link:** [An unsupervised map of excitatory neuron dendritic morphology in the mouse visual cortex](https://www.nature.com/articles/s41467-025-58763-w?error=cookies_not_supported&code=0627d9e2-bb90-4d73-81e6-7fb6d35a7787) \n- **Summary:** Data-driven, graph\u2011based analysis of >30,000 excitatory neuron reconstructions from MICrONS EM volumes. Proposes a continuum (axes of variation) rather than discrete morphological m-types for excitatory cortical neurons; highlights laminar and inter-area morphological trends. \n- **Preview image:** \n ![Fig from Ecker et al. 2025 \u2014 dendritic morphology mapping](https://media.springernature.com/m685/springer-static/image/art%3A10.1038%2Fs41467-025-58763-w/MediaObjects/41467_2025_58763_Fig1_HTML.png)\n\n---\n\n### 3) Single-neuron analysis of dendrites and axons reveals the network organization in mouse prefrontal cortex \n- **Author:** Yan, Jun et al. \n- **Published:** 2023-05-22 \n- **Link:** [Single-neuron analysis of dendrites and axons \u2014 mouse PFC (Nature Neuroscience 2023)](https://www.nature.com/articles/s41593-023-01339-y?error=cookies_not_supported&code=8b2e74bb-a437-4a3b-8529-0bb7d0312cad) \n- **Summary:** Complete dendrite and axon reconstructions of nearly 2,000 PFC neurons; classification of dendritic subtypes and joint dendrite\u2013axon analyses revealing potential intra-column, inter-hemispheric, and inter-column connectivity organization. \n- **Preview image:** \n ![Fig from Yan et al. 2023 \u2014 PFC neuron reconstructions](https://media.springernature.com/m685/springer-static/image/art%3A10.1038%2Fs41593-023-01339-y/MediaObjects/41593_2023_1339_Fig1_HTML.png)\n\n---\n\n### 4) Modeling and Simulation of Neocortical Micro- and Mesocircuitry. Part I: Anatomy (preprint / reviewed preprint) \n- **Authors:** Markram, H. et al. (large consortium) \n- **Published:** 2024-08-13 (reviewed preprint) \n- **Link:** [eLife reviewed preprint \u2014 Anatomy of neocortical micro- and mesocircuitry](https://elifesciences.org/reviewed-preprints/99688v1/pdf) \n- **Summary:** Data-driven anatomical model of juvenile rat somatosensory cortex with 4.2M morphologically-detailed neuron models and 14.2B synapses. Focuses on placement and connectivity rules \u2014 useful for how neuron morphology constrains circuit connectivity. \n- **Preview image:** Not provided in search result.\n\n---\n\n### 5) Cell class-specific long-range axonal projections of neurons in mouse whisker-related somatosensory cortices (preprint / reviewed preprint) \n- **Authors:** Aronoff, R. P.; Petersen, C. C. H.; others \n- **Published:** 2024-05-17 (reviewed preprint) \n- **Link:** [eLife reviewed preprint \u2014 class-specific long-range axonal projections](https://elifesciences.org/reviewed-preprints/97602v1/pdf) \n- **Summary:** Brain-wide light-sheet imaging of genetically defined projection neurons; quantifies long-range axonal innervation patterns mapped to the Allen CCF. Links axonal projection patterns to functional propagation measured with optogenetics and wide-field imaging. \n- **Preview image:** Not provided in search result.\n\n---\n\n### 6) NeuroFly: A framework for whole-brain single neuron reconstruction (preprint) \n- **Authors:** Rubin Zhao et al. \n- **Published:** 2023-01-31 (arXiv) \n- **Link:** [NeuroFly (arXiv 2411.04715v1)](https://arxiv.org/html/2411.04715v1) \n- **Summary:** Computational framework (segmentation, connection, proofreading) for large-scale automated single neuron reconstruction from whole-brain imaging; aims to accelerate collection of neuronal morphologies at scale. \n- **Preview image:** Not provided in search result.\n\n---\n\n## Notes and next steps\n- The search returned 6 results above; I can retrieve more results or expand the time window if you want additional papers. \n- If you want the full text (PDF) for any of the listed items and the paper is publicly accessible, I can attempt to fetch and extract the full article using the read-paper tool \u2014 tell me which item(s) you want read in full.", "actual_tool_calls": [ { "name": "literature-search-tool", "arguments": { - "query": "neuron morphology cerebrum 2020..2025 neuron reconstruction cortical morphology pyramidal interneuron", - "num_results": 8 - } - }, - { - "name": "web-search-tool", - "arguments": { - "query": "recent studies neuron morphology cerebrum 2020 2021 2022 2023 2024 pyramidal neuron morphology cortex reconstruction", + "query": "neuron morphology cerebrum 2022..2025 OR 2023..2025 neuron morphology cerebral cortex single-cell reconstruction dendrite axon", + "start_publish_date": "2022-01-01T00:00:00Z", + "end_publish_date": "2025-12-01T00:00:00Z", "num_results": 6 } } @@ -917,10 +907,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.4840374574429811, - "success": false, + "score": 0.8922801826803916, + "success": true, "threshold": 0.5, - "reason": "The actual output demonstrates an understanding of the topic by indicating a plan to search for recent studies on neuron morphology in the cerebrum, which aligns with the input request. However, it fails to provide the expected structured output with specific studies, titles, authors, summaries, and links, resulting in a lack of completeness and coherence in fulfilling the request." + "reason": "The actual output closely aligns with the expected output by providing a structured list of recent studies on neuron morphology in the cerebrum, including relevant details such as authors, publication dates, summaries, and links. It effectively addresses the input question and maintains coherence throughout. The only minor shortcoming is that it includes specific details instead of the placeholder format requested in the expected output, which slightly deviates from the exact structure." }, { "name": "Tool Correctness", @@ -941,7 +931,7 @@ "score": 0.0, "success": false, "threshold": 0.5, - "reason": "Incomplete tool usage: missing tools [ToolCall(\n name=\"literature-search-tool\",\n input_parameters={\n \"query\": \"neuron morphology cerebrum\",\n \"numResults\": 5,\n \"maxCharacters\": 1000\n }\n)]; expected ['literature-search-tool'], called ['literature-search-tool', 'web-search-tool']. See more details above." + "reason": "Incomplete tool usage: missing tools [ToolCall(\n name=\"literature-search-tool\",\n input_parameters={\n \"query\": \"neuron morphology cerebrum\",\n \"numResults\": 5,\n \"maxCharacters\": 1000\n }\n)]; expected ['literature-search-tool'], called ['literature-search-tool']. See more details above." }, { "name": "Overall Argument Correctness", @@ -951,7 +941,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.263872" + "created_at": "2025-12-09 11:28:17.258400" } }, "ion_channel": { @@ -971,7 +961,7 @@ "ion_channel" ] }, - "ai_response": "To find ion channels linked to the gene *Asic3*, I'll query the ion-channel database filtering by the gene name so we retrieve all matching channel entries and their metadata. Next, I'll run a targeted search for ion channels where gene = \\Asic3\\\".## Search plan\\nI queried the ion-channel knowledge graph for entries where the gene equals *Asic3* to retrieve all ion channels annotated to that gene and their metadata. With that result, I will summarize the matched channel(s) and provide key identifiers and synonyms.\\n\\n## Results \\u2014 Ion channels related to the gene *Asic3*\\nTotal items found: **1**\\n\\n| Field | Value |\\n|-------|-------|\\n| **Label** | **ASIC3** |\\n| **ID** | **1bdf731a-df80-48ad-9f06-ed92906221d4** |\\n| **Name** | ASIC3 |\\n| **Gene** | *Asic3* |\\n| **Synonyms** | DRASIC; TNaC1; ACCN3; amiloride-sensitive cation channel 3, testis; testis sodium channel 1; acid sensing ion channel 3; dorsal root ASIC; acid-sensing (proton-gated) ion channel 3; acid sensing (proton gated) ion channel 3 |\\n| **Description** | (empty) |\\n| **Created by** | Eleftherios Zisis (ID: 6654bb3e-a44b-4fbd-bcd4-44d2ba2eba01) |\\n| **Updated by** | Eleftherios Zisis (ID: 6654bb3e-a44b-4fbd-bcd4-44d2ba2eba01) |\\n| **Creation date** | 2025-09-19T07:49:01.188929Z |\\n| **Update date** | 2025-09-19T07:49:01.188929Z |\\n\\n### Notes\\n- This query returned a single ion-channel entity annotated to gene *Asic3*.\\n- No external asset link (url_link) was provided in the returned record.\\n\\nWould you like me to retrieve detailed information for this channel (using its ID), search for related literature about ASIC3, or expand the search (e.g., include related genes or species-specific variants)?", + "ai_response": "## Results \u2014 ion channels related to **Asic3**\n\n**Query summary:** searched the EntityCore ion-channel database for entries with **gene = Asic3**.\n\n**Total items found:** **1**\n\n### 1) **ASIC3** \n- **ID:** **1bdf731a-df80-48ad-9f06-ed92906221d4** \n- **Name / Label:** **ASIC3** \n- **Gene:** **Asic3** \n- **Description:** *(empty in record)* \n- **Synonyms:** DRASIC; TNaC1; ACCN3; amiloride-sensitive cation channel 3, testis; testis sodium channel 1; acid sensing ion channel 3; dorsal root ASIC; acid-sensing (proton-gated) ion channel 3; acid sensing (proton gated) ion channel 3 \n- **Created by:** **Eleftherios Zisis** (ID: 6654bb3e-a44b-4fbd-bcd4-44d2ba2eba01) \n- **Updated by:** **Eleftherios Zisis** (same as above) \n- **Creation date:** 2025-09-19T07:49:01.188929Z \n- **Update date:** 2025-09-19T07:49:01.188929Z\n\nNote: the EntityCore response did not include an asset URL/link field for this entry.\n\nWould you like any of the following next steps?\n- Retrieve the detailed ion-channel model(s) linked to this channel (models, measurements, assets). \n- List ion-channel recordings associated with this channel. \n- Expand synonyms/metadata or export this entry (JSON).", "actual_tool_calls": [ { "name": "entitycore-ionchannel-getall", @@ -985,10 +975,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.7807403647069058, + "score": 0.7820330992678872, "success": true, "threshold": 0.5, - "reason": "The actual output closely follows the expected output structure, providing relevant details about the ion channel associated with the gene Asic3. It includes the name, ID, label, gene, synonyms, and creator information, which aligns well with the expected content. However, the output is slightly verbose and includes unnecessary details about the search process, which detracts from the clarity and conciseness of the response. Additionally, the summary format differs slightly from the expected output, which affects the overall coherence." + "reason": "The actual output is relevant to the input, providing detailed information about the ion channel related to the gene Asic3. It includes all necessary sections such as ID, Name, Gene, Synonyms, and creator information. However, the structure differs slightly from the expected output, particularly in the presentation of the summary and the absence of a direct entity link, which affects coherence with the expected format." }, { "name": "Tool Correctness", @@ -1002,7 +992,7 @@ "score": 1.0, "success": true, "threshold": 0.5, - "reason": "The score is 1.00 because the input was clear and directly related to the request for information about ion channels associated with the Asic3 gene, and there were no incorrect tool calls." + "reason": "The score is 1.00 because there were no incorrect tool calls and the input was clear and specific." }, { "name": "Deterministic Argument Correctness", @@ -1019,7 +1009,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.263904" + "created_at": "2025-12-09 11:28:17.258416" } }, "platform_viewing": { @@ -1036,7 +1026,7 @@ "platform_ui" ] }, - "ai_response": "I will check the page context to identify which entity or view you're currently looking at so I can explain its contents and purpose. Using the page analyzer will tell me the entity type, ID, and relevant metadata that I can then describe.## Quick identification \\u2014 what you are looking at\\n\\nYou are currently viewing the **Cell Morphology** browser for a specific brain region on the Open Brain Platform.\\n\\n### Key details from the page context\\n- **Page / view:** cell-morphology listing (browse of cell morphology entities) \\n- **Brain region ID:** **2a156e47-0842-4a40-bd1e-2afffb4dbafd** \\n- **Additional query parameter:** **br_av = 477** (an atlas/version parameter associated with the brain region query)\\n\\nGiven this context, the page is showing (or is set up to show) morphology datasets \\u2014 reconstructed neuron shapes (soma, dendrites, axons) \\u2014 that were recorded or grouped for the specified brain region.\\n\\n## What you can do next\\n- If you want, I can list the cell morphology entries available for that brain region (total count and per-item metadata). To retrieve those entries I will query the EntityCore database for cell morphologies within the brain region. \\n Reasoning: retrieving the list will let me show names, IDs, and quick metadata so you can pick a specific morphology to inspect in detail.\\n\\nWould you like me to fetch the list of cell morphology entities in that brain region now?", + "ai_response": "## Summary of what you're looking at\n\nYou are viewing a **Cell Morphology** page for a specific brain region on the Open Brain Platform. The page context indicates:\n\n- **Entity type:** *cell-morphology* \n- **Brain region ID:** **2a156e47-0842-4a40-bd1e-2afffb4dbafd** \n- **Current entity:** *(no single entity open \u2014 you\u2019re on the collection / listing view)*\n\n### What \"Cell Morphology\" means here\n- A **cell morphology** entry is a digital representation of a neuron's shape and arbors (soma, dendrites, axon). \n- Typical files and assets for a morphology include reconstruction files (e.g., SWC), preview images, metadata (species, brain region, cell type), and provenance (who uploaded it, experimental details).\n\n### What the page likely shows you\n- A list or gallery of neuron reconstructions attributed to the selected brain region. \n- For each entry you can usually find: \n - **Name / ID** of the morphology \n - **Thumbnail** or preview image (if available) \n - Basic metadata (species, brain region label, cell class/type) \n - Links to download or view assets (reconstruction file, images) \n- Since **current_entity_id** is null, you\u2019re not looking at a single morphology\u2019s detail page but at the collection/filtered listing for that brain region.\n\n## Next steps I can take for you\n- I can retrieve and list the morphologies in that brain region (names, IDs, and available assets). This will show how many items there are and let you pick one to inspect in detail. \n- I can also fetch a single morphology\u2019s details if you click/open one and tell me which one to inspect.\n\nWould you like me to list the cell morphologies in this brain region now?", "actual_tool_calls": [ { "name": "context-analyzer-tool", @@ -1047,10 +1037,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.6731008652407333, + "score": 0.72747771401505, "success": true, "threshold": 0.5, - "reason": "The actual output provides relevant information about the Cell Morphology browser on the Open Brain Platform, addressing the user's query effectively. It includes key details such as the brain region ID and additional parameters, which align with the expected output's focus on context. However, it lacks some structural elements and specific content details found in the expected output, such as the typical contents of a 'cell morphology' entry and the significance of the brain region ID, which affects completeness." + "reason": "The actual output provides a detailed summary of the Cell Morphology page on the Open Brain Platform, addressing the topic of neuroscience effectively. It includes relevant sections such as entity type, brain region ID, and descriptions of what a cell morphology entry contains. However, it lacks some specific details present in the expected output, such as the mention of the brain region annotation value and quantitative measurements. Overall, it aligns well with the expected structure and content but could improve in accuracy and completeness." }, { "name": "Tool Correctness", @@ -1081,7 +1071,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.263934" + "created_at": "2025-12-09 11:28:17.258432" } }, "ion_channel_recording": { @@ -1107,19 +1097,20 @@ "ion_channel" ] }, - "ai_response": "I will first locate the brain region ID(s) corresponding to \\thalamus\\ in the platform ontology so I can then search ion-channel recordings within that region. I'll run a semantic search for \\thalamus\\ to retrieve matching brain region entries.I will use the Thalamus region ID returned above to search for ion-channel recordings located within that brain region. I'll query the ion-channel-recordings endpoint filtering by the Thalamus brain region ID.## Summary \\u2014 search plan and outcome\\n\\nI used the platform ontology to resolve the brain region for *thalamus* and then retrieved ion-channel-recording entries that are annotated as being within that region. Below I summarize the region used and the first page of matching ion-channel recordings, and note the total number of matches so you can decide whether to fetch more.\\n\\n- Region searched: **Thalamus** \\u2014 **ID: 75e3944e-8dcd-4310-a3a2-258f93de63d6**\\n- Ion channel recordings found (page 1): **10 items shown**\\n- Total ion channel recordings matching the Thalamus region query: **33,198 total_items**\\n\\nWould you like me to fetch more results (next page) or filter these results (by ion channel name, species, recording_type, date, temperature, or author)?\\n\\n---\\n\\n## First 10 ion channel recordings (page 1)\\n\\nBelow are the first 10 records returned for the Thalamus search. For each entry I include the key metadata and the platform link.\\n\\n### 1) **rCell25437** \\u2014 **ID: 38586d7f-6d11-4a3e-91f5-fe41b788697c**\\n- **Ion channel:** Kv6.2 (gene: Kcng2) \\u2014 **ID: 426165d8-f96f-4f95-9e5e-eac6fb081993**\\n- **Type:** intracellular (voltage clamp)\\n- **Origin:** in_vitro\\n- **Temperature:** 25.0 \\u00b0C\\n- **Cell line:** CHO\\n- **Subject species:** *Rattus norvegicus* (Generic)\\n- **Experiment date:** 2018-03-20\\n- **Asset (label):** nwb \\u2014 path: rCell25437.nwb\\n- **License:** CC BY 4.0\\n- **Platform link:** [View rCell25437](https://openbraininstitute.org/app/entity/38586d7f-6d11-4a3e-91f5-fe41b788697c)\\n\\n### 2) **rCell25609** \\u2014 **ID: 562138e9-eb4e-4dc3-a3c6-018096aedd5a**\\n- **Ion channel:** Kv6.2 (Kcng2)\\n- **Type:** intracellular\\n- **Origin:** in_vitro\\n- **Temperature:** 15.0 \\u00b0C\\n- **Cell line:** CHO\\n- **Subject species:** *Rattus norvegicus* (Generic)\\n- **Experiment date:** 2018-03-21\\n- **Asset (label):** nwb \\u2014 path: rCell25609.nwb\\n- **Platform link:** [View rCell25609](https://openbraininstitute.org/app/entity/562138e9-eb4e-4dc3-a3c6-018096aedd5a)\\n\\n### 3) **rCell25635** \\u2014 **ID: 6bf9d4be-ab56-4c24-9b41-d9c1599b5eb8**\\n- **Ion channel:** Kv6.2 (Kcng2)\\n- **Type:** intracellular\\n- **Origin:** in_vitro\\n- **Temperature:** 35.0 \\u00b0C\\n- **Cell line:** CHO\\n- **Subject species:** *Rattus norvegicus* (Generic)\\n- **Experiment date:** 2018-03-22\\n- **Asset (label):** nwb \\u2014 path: rCell25635.nwb\\n- **Platform link:** [View rCell25635](https://openbraininstitute.org/app/entity/6bf9d4be-ab56-4c24-9b41-d9c1599b5eb8)\\n\\n### 4) **rCell25621** \\u2014 **ID: a070082f-5fd2-4487-a742-72f007167cf9**\\n- **Ion channel:** Kv6.2 (Kcng2)\\n- **Type:** intracellular\\n- **Origin:** in_vitro\\n- **Temperature:** 15.0 \\u00b0C\\n- **Cell line:** CHO\\n- **Subject species:** *Rattus norvegicus* (Generic)\\n- **Experiment date:** 2018-03-21\\n- **Asset (label):** nwb \\u2014 path: rCell25621.nwb\\n- **Platform link:** [View rCell25621](https://openbraininstitute.org/app/entity/a070082f-5fd2-4487-a742-72f007167cf9)\\n\\n### 5) **rCell25580** \\u2014 **ID: a9eeebd2-dac9-4235-a7b9-bb1a659efdb8**\\n- **Ion channel:** Kv6.2 (Kcng2)\\n- **Type:** intracellular\\n- **Origin:** in_vitro\\n- **Temperature:** 15.0 \\u00b0C\\n- **Cell line:** CHO\\n- **Subject species:** *Rattus norvegicus* (Generic)\\n- **Experiment date:** 2018-03-21\\n- **Asset (label):** nwb \\u2014 path: rCell25580.nwb\\n- **Platform link:** [View rCell25580](https://openbraininstitute.org/app/entity/a9eeebd2-dac9-4235-a7b9-bb1a659efdb8)\\n\\n### 6) **rCell25594** \\u2014 **ID: 04e04a19-e430-49d5-8ff2-a5439a546ffb**\\n- **Ion channel:** Kv6.2 (Kcng2)\\n- **Type:** intracellular\\n- **Origin:** in_vitro\\n- **Temperature:** 15.0 \\u00b0C\\n- **Cell line:** CHO\\n- **Subject species:** *Rattus norvegicus* (Generic)\\n- **Experiment date:** 2018-03-21\\n- **Asset (label):** nwb \\u2014 path: rCell25594.nwb\\n- **Platform link:** [View rCell25594](https://openbraininstitute.org/app/entity/04e04a19-e430-49d5-8ff2-a5439a546ffb)\\n\\n### 7) **rCell25595** \\u2014 **ID: 2c0809a5-51be-4e9f-a2bf-0151258b8f67**\\n- **Ion channel:** Kv6.2 (Kcng2)\\n- **Type:** intracellular\\n- **Origin:** in_vitro\\n- **Temperature:** 15.0 \\u00b0C\\n- **Cell line:** CHO\\n- **Subject species:** *Rattus norvegicus* (Generic)\\n- **Experiment date:** 2018-03-21\\n- **Asset (label):** nwb \\u2014 path: rCell25595.nwb\\n- **Platform link:** [View rCell25595](https://openbraininstitute.org/app/entity/2c0809a5-51be-4e9f-a2bf-0151258b8f67)\\n\\n### 8) **rCell25620** \\u2014 **ID: 060e99b3-ebf4-49db-bfa4-55230f11b713**\\n- **Ion channel:** Kv6.2 (Kcng2)\\n- **Type:** intracellular\\n- **Origin:** in_vitro\\n- **Temperature:** 15.0 \\u00b0C\\n- **Cell line:** CHO\\n- **Subject species:** *Rattus norvegicus* (Generic)\\n- **Experiment date:** 2018-03-21\\n- **Asset (label):** nwb \\u2014 path: rCell25620.nwb\\n- **Platform link:** [View rCell25620](https://openbraininstitute.org/app/entity/060e99b3-ebf4-49db-bfa4-55230f11b713)\\n\\n### 9) **rCell25634** \\u2014 **ID: b34a2d1b-0b79-4029-805c-d95beedff0c4**\\n- **Ion channel:** Kv6.2 (Kcng2)\\n- **Type:** intracellular\\n- **Origin:** in_vitro\\n- **Temperature:** 35.0 \\u00b0C\\n- **Cell line:** CHO\\n- **Subject species:** *Rattus norvegicus* (Generic)\\n- **Experiment date:** 2018-03-22\\n- **Asset (label):** nwb \\u2014 path: rCell25634.nwb\\n- **Platform link:** [View rCell25634](https://openbraininstitute.org/app/entity/b34a2d1b-0b79-4029-805c-d95beedff0c4)\\n\\n### 10) **rCell25581** \\u2014 **ID: 329b1662-199b-40df-847c-e1cfa9b54306**\\n- **Ion channel:** Kv6.2 (Kcng2)\\n- **Type:** intracellular\\n- **Origin:** in_vitro\\n- **Temperature:** 15.0 \\u00b0C\\n- **Cell line:** CHO\\n- **Subject species:** *Rattus norvegicus* (Generic)\\n- **Experiment date:** 2018-03-21\\n- **Asset (label):** nwb \\u2014 path: rCell25581.nwb\\n- **Platform link:** [View rCell25581](https://openbraininstitute.org/app/entity/329b1662-199b-40df-847c-e1cfa9b54306)\\n\\n---\\n\\n## Notes and next steps\\n\\n- The returned records on page 1 are primarily Kv6.2 recordings (CHO cell-line, in vitro). Given the very large total (33,198 items), many entries likely come from different projects / regions annotated under the Thalamus hierarchy \\u2014 we can narrow the results by any of these common filters: **ion channel (name or gene)**, **species**, **recording_type**, **experiment_date range**, **temperature**, **contributor**, or **authorized_public** status.\\n- If you want more entries, I can:\\n - Retrieve the next page(s) (page_size up to 10 per request). \\n - Apply filters (tell me which filter(s) you prefer).\\n - Return a CSV or table view of selected fields for a larger set.\\n\\nTell me which of the above you'd like next (fetch next page, apply filters, or export selected fields).", + "ai_response": "## Search results \u2014 Ion channel recordings in the Thalamus\n\n**Resolved brain region**\n- **Name:** Thalamus \n- **ID:** **75e3944e-8dcd-4310-a3a2-258f93de63d6** \n- **Annotation value:** **549**\n\nGiven the Thalamus ID above, I queried the platform for ion-channel recordings within that region (including ascendants and descendants).\n\n### Summary\n- **Total ion-channel recordings found:** **0**\n\nNo ion-channel-recording entries were returned for the Thalamus (ID: **75e3944e-8dcd-4310-a3a2-258f93de63d6**) in the first page of results.\n\n### Next steps you can choose\n- Request that I expand the search (e.g., search nearby/related regions such as specific thalamic nuclei like *ventral posterolateral nucleus*, *mediodorsal nucleus*, etc.). I can resolve those region IDs and query them individually. \n- Search without restricting to brain region but filter by keywords (ion channel name, gene, or recording type) to find thalamus-related recordings that may lack explicit region annotations. \n- Ask me to run pagination or broader queries (note: current query returned total_items = 0, so pagination will not add results for this exact filter).\n\nTell me which option you prefer, or specify particular thalamic nuclei, ion channels, or recording properties to refine the search.", "actual_tool_calls": [ { "name": "entitycore-brainregion-getall", "arguments": { - "semantic_search": "thalamus", - "page_size": 10 + "semantic_search": "thalamus" } }, { "name": "entitycore-ionchannelrecording-getall", "arguments": { "page_size": 10, + "brain_region__annotation_value": 549, + "brain_region__hierarchy_id": "e3e70682-c209-4cac-a29f-6fbed82c07cd", "within_brain_region_brain_region_id": "75e3944e-8dcd-4310-a3a2-258f93de63d6" } } @@ -1128,10 +1119,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.8749718614233014, + "score": 0.5556731982805367, "success": true, "threshold": 0.5, - "reason": "The actual output closely aligns with the expected output, providing a detailed search plan and results for ion channel recordings in the thalamus. It includes the brain region ID, the total number of recordings, and a summary of the first 10 recordings with relevant metadata. However, it slightly deviates from the expected format by including additional details in the summary and notes sections, which, while informative, are not explicitly requested in the expected output." + "reason": "The actual output is relevant to the input, as it addresses ion channel recordings in the thalamus. However, it lacks the expected structure, particularly the detailed list of recordings that should be present. While it correctly identifies that no recordings were found, it does not provide the expected format for the summary or the detailed entries for recordings, which diminishes its alignment with the expected output." }, { "name": "Tool Correctness", @@ -1149,10 +1140,10 @@ }, { "name": "Deterministic Argument Correctness", - "score": 0.5, + "score": 0.625, "success": true, "threshold": 0.5, - "reason": "Incomplete tool usage: missing tools [ToolCall(\n name=\"entitycore-brainregion-getall\",\n input_parameters={\n \"semantic_search\": \"thalamus\"\n }\n), ToolCall(\n name=\"entitycore-ionchannelrecording-getall\",\n input_parameters={\n \"within_brain_region_brain_region_id\": \"75e3944e-8dcd-4310-a3a2-258f93de63d6\"\n }\n)]; expected ['entitycore-brainregion-getall', 'entitycore-ionchannelrecording-getall'], called ['entitycore-brainregion-getall', 'entitycore-ionchannelrecording-getall']. See more details above." + "reason": "Incomplete tool usage: missing tools [ToolCall(\n name=\"entitycore-ionchannelrecording-getall\",\n input_parameters={\n \"within_brain_region_brain_region_id\": \"75e3944e-8dcd-4310-a3a2-258f93de63d6\"\n }\n)]; expected ['entitycore-brainregion-getall', 'entitycore-ionchannelrecording-getall'], called ['entitycore-brainregion-getall', 'entitycore-ionchannelrecording-getall']. See more details above." }, { "name": "Overall Argument Correctness", @@ -1162,7 +1153,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.263965" + "created_at": "2025-12-09 11:28:17.258448" } }, "off_topic_cooking": { @@ -1175,16 +1166,16 @@ "rejection" ] }, - "ai_response": "\\\"I'm specialized in neuroscience and the Open Brain Platform. I can't help with that topic, but I'd be happy to assist with brain research or platform-related questions.\\\"\\n\\That's outside my area of expertise. I focus specifically on neuroscience and the Open Brain Platform. Is there anything related to brain data or the platform I can help you with?\\", + "ai_response": "I specialize in neuroscience and the Open Brain Platform. That topic isn\u2019t within my scope, but I can help if you\u2019re looking for brain-related cooking experiments or how dietary choices affect cognition.\n\nIf you want cookie help, you could try a general kitchen answer. For brain-friendly cookies, I can suggest a recipe that uses ingredients with cognitive-benefit profiles (e.g., oats, dark chocolate, nuts) and outline a simple bake process. Let me know if you\u2019d like that.", "actual_tool_calls": [], "results": { "metrics": [ { "name": "Correctness [GEval]", - "score": 0.5945247609273225, + "score": 0.5978033804771575, "success": true, "threshold": 0.5, - "reason": "The actual output is relevant to the input in that it clearly states the focus on neuroscience and the Open Brain Platform, which aligns with the expected output's emphasis on expertise. However, the structure is slightly disorganized, as it includes unnecessary repetition and additional phrases that detract from the clarity of the response. While it does provide a relevant response, it does not match the expected output in terms of conciseness and directness." + "reason": "The actual output acknowledges the input about chocolate chip cookies but redirects the conversation towards neuroscience, which aligns with the evaluator's focus. However, it diverges from the expected output by providing additional suggestions for brain-friendly cookies, which is not present in the expected response. While it maintains relevance to neuroscience, the structure and content deviate from the expected brevity and directness, leading to a moderate score." }, { "name": "Tool Correctness", @@ -1215,7 +1206,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.263994" + "created_at": "2025-12-09 11:28:17.258463" } }, "read_paper": { @@ -1236,14 +1227,13 @@ "read_paper" ] }, - "ai_response": "I will retrieve the full content of the PubMed page you gave to extract what you want from it; next I will fetch and parse the page at that exact URL to display its content and any images present.## Response \\u2014 unable to fetch that exact page directly\\n\\n**Short reasoned step:** The direct page you gave (https://pubmed.ncbi.nlm.nih.gov/) cannot be retrieved with the document-extraction tool I attempted to use, so I can't read or parse that URL as-is.\\n\\n### What I can do for you\\nChoose one of the options below and I will proceed:\\n\\n1. **Send a specific PubMed ID (PMID) or full article URL** \\n - If you provide the PMID (e.g., 12345678) or the full article URL (the page for a specific paper), I can fetch and summarize that article. \\n - Next step (if you give a PMID/URL): I will retrieve the paper page and extract title, authors, abstract, figures (if available), and key points.\\n\\n2. **Ask me to search PubMed for papers on a topic** \\n - Tell me keywords, authors, or a time window and I will search the literature (I can use the platform literature search). \\n - Next step (if you give a query): I will run a targeted literature search and return matching papers with key metadata and images (when available).\\n\\n3. **If you meant the PubMed homepage and want guidance** \\n - I can't browse the PubMed website homepage directly, but I can explain typical things found there (search bar, filters) or help craft search queries for PubMed. \\n - Next step (if you confirm): I will outline recommended search strategies and example queries tailored to your topic.\\n\\n### Quick note on images and full texts\\n- If the paper entries include image URLs, I will embed them in the response.\\n- If you want full-text extraction of a paper that is publicly available, I can attempt to read the full article after locating its link.\\n\\nTell me which option you prefer (and provide a PMID, article URL, or search query if applicable), and I will proceed.", + "ai_response": "## Plan / next step\nI first retrieved the PubMed home page to extract the visible structure, main features, and any prominent links or content so I can give you a concise guided overview of what is on that page.\n\n---\n\n## Summary of https://pubmed.ncbi.nlm.nih.gov/\n\n![PubMed meta image](https://cdn.ncbi.nlm.nih.gov/pubmed/persistent/pubmed-meta-image-v2.jpg)\n\n**Site:** PubMed \u2014 an official U.S. government (NIH / NLM) resource \n**Short description (from page):** PubMed comprises more than **39 million citations** for biomedical literature from MEDLINE, life science journals, and online books. Citations may include links to full text in PubMed Central and publisher sites.\n\n### Main visible sections and links\n- **Search bar / Advanced search**\n - Search input and link to **Advanced** search: https://pubmed.ncbi.nlm.nih.gov/advanced/\n - Quick access to search results and query features.\n\n- **Account / My NCBI**\n - **Log in** / account links: https://account.ncbi.nlm.nih.gov \n - When logged in, page shows account menu items such as:\n - **Dashboard**: https://pubmed.ncbi.nlm.nih.gov/myncbi/\n - **Publications**: https://pubmed.ncbi.nlm.nih.gov/myncbi/collections/bibliography/\n - **Account settings** and **Log out**\n\n- **Learn**\n - **About PubMed**: https://pubmed.ncbi.nlm.nih.gov/about/\n - **FAQs & User Guide**: https://pubmed.ncbi.nlm.nih.gov/help/\n - **Finding Full Text**: https://support.nlm.nih.gov/knowledgebase/article/KA-03304/en-us/\n\n- **Find / Tools**\n - **Advanced Search** (again): https://pubmed.ncbi.nlm.nih.gov/advanced/\n - **Clinical Queries**: https://pubmed.ncbi.nlm.nih.gov/clinical/\n - **Single Citation Matcher**: https://pubmed.ncbi.nlm.nih.gov/citmatch/\n\n- **Download / APIs**\n - **E-utilities API** documentation: https://www.ncbi.nlm.nih.gov/books/NBK25497/\n - **FTP / Downloads**: https://pubmed.ncbi.nlm.nih.gov/download/\n - **Batch Citation Matcher**: https://pubmed.ncbi.nlm.nih.gov/batchcitmatch/\n\n- **Explore**\n - **MeSH Database**: https://www.ncbi.nlm.nih.gov/mesh/\n - **Journals / NLM Catalog**: https://www.ncbi.nlm.nih.gov/nlmcatalog/journals/\n\n### Notices on the page (visible text)\n- Temporary feature notice: \u201cClipboard, Search History, and several other advanced features are temporarily unavailable.\u201d (visible on the page snapshot)\n- Security and authenticity indicators with government/site badges:\n - Dot-gov and HTTPS icons indicating the official and secure site.\n\n### Example: Trending Articles (as shown)\n- The page shows a \u201cTrending Articles\u201d snippet with entries such as:\n - \u201c[Elaboration of a hearing disability scale].\u201d \u2014 Cuda D, et al. Acta Otorhinolaryngol Ital. 1993. PMID: 8135102 \n - (Entry links to: https://pubmed.ncbi.nlm.nih.gov/8135102/)\n\n---\n\n## What I can do next\n- Provide a short walkthrough of how to run an Advanced search for a specific topic, author, or PMID using the Advanced Search page.\n- Retrieve and summarize a specific PubMed article page (give me a PMID or article title).\n- Extract details about the E-utilities API or give example queries for programmatic access.\n\nWhich of these would you like me to do next?", "actual_tool_calls": [ { - "name": "read-paper", + "name": "web-search-tool", "arguments": { - "urls": [ - "https://pubmed.ncbi.nlm.nih.gov/" - ] + "query": "https://pubmed.ncbi.nlm.nih.gov/", + "num_results": 1 } } ], @@ -1251,41 +1241,41 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.32354476156942, - "success": false, + "score": 0.7122372263400929, + "success": true, "threshold": 0.5, - "reason": "The actual output acknowledges the inability to retrieve the PubMed homepage directly, which is relevant to the input. However, it fails to provide a summary of the homepage content as expected. While it offers alternative options for the user, it does not address the key points or sections outlined in the expected output, leading to a significant gap in completeness and appropriateness of the information provided." + "reason": "The actual output provides a detailed summary of the PubMed homepage, including key sections and features, which aligns with the input's request for an overview. However, it lacks some specific elements from the expected output, such as the mention of 'PubMed Updates' and the inclusion of trending articles with specific examples. While the response is relevant and structured well, it could improve by incorporating all expected details." }, { "name": "Tool Correctness", - "score": 1.0, - "success": true, + "score": 0.0, + "success": false, "threshold": 0.5, - "reason": "Correct ordering: all expected tools ['read-paper'] were called in the correct order." + "reason": "Incorrect tool usage: missing tools ['read-paper'] and out-of-order tools ['read-paper']; expected ['read-paper'], called ['web-search-tool']. See more details above." }, { "name": "Argument Correctness", - "score": 1.0, - "success": true, + "score": 0.0, + "success": false, "threshold": 0.5, - "reason": "The score is 1.00 because there are no incorrect tool calls or issues with the input." + "reason": "The score is 0.00 because there was no input parameter provided, which is essential for the tool to function correctly." }, { "name": "Deterministic Argument Correctness", - "score": 1.0, - "success": true, + "score": 0.0, + "success": false, "threshold": 0.5, - "reason": "All expected tools ['read-paper'] were called (order not considered)." + "reason": "Incomplete tool usage: missing tools [ToolCall(\n name=\"read-paper\",\n input_parameters={\n \"urls\": [\n \"https://pubmed.ncbi.nlm.nih.gov/\"\n ]\n }\n)]; expected ['read-paper'], called ['web-search-tool']. See more details above." }, { "name": "Overall Argument Correctness", - "score": 1.0, - "success": true, + "score": 0.0, + "success": false, "threshold": 0.5, "reason": "" } ], - "created_at": "2025-11-28 16:33:30.264023" + "created_at": "2025-12-09 11:28:17.258478" } }, "platform_explore": { @@ -1297,16 +1287,16 @@ "platform_ui" ] }, - "ai_response": "I'm specialized in neuroscience and the Open Brain Platform. I can't help with that topic, but I'd be happy to assist with brain research or platform-related questions.\\nThat's outside my area of expertise. I focus specifically on neuroscience and the Open Brain Platform. Is there anything related to brain data or the platform I can help you with?", + "ai_response": "\"I'm specialized in neuroscience and the Open Brain Platform. I can't help with that topic, but I'd be happy to assist with brain research or platform-related questions.\"\n\n\"That's outside my area of expertise. I focus specifically on neuroscience and the Open Brain Platform. Is there anything related to brain data or the platform I can help you with?\"", "actual_tool_calls": [], "results": { "metrics": [ { "name": "Correctness [GEval]", - "score": 0.2853000957950641, + "score": 0.27294881512648356, "success": false, "threshold": 0.5, - "reason": "The actual output acknowledges its specialization in neuroscience and the Open Brain Platform, which is relevant to the input. However, it fails to provide specific guidance on locating the Explore button, which is a key aspect of the expected output. The structure is somewhat aligned, but the content lacks the necessary detail and actionable recommendations found in the expected response." + "reason": "The actual output acknowledges its specialization in neuroscience and the Open Brain Platform, which aligns with the input topic. However, it fails to provide any specific guidance on locating the Explore button, which is a critical aspect of the expected output. The response is polite but lacks the necessary information and structure to be fully relevant or helpful, resulting in a low score." }, { "name": "Tool Correctness", @@ -1337,7 +1327,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.264051" + "created_at": "2025-12-09 11:28:17.258493" } }, "cerebellum_morphologies_descendants": { @@ -1363,7 +1353,7 @@ "entitycore" ] }, - "ai_response": "I will first find the Cerebellum brain-region ID (using a semantic search) so I can limit the morphology query to its descendant regions. After that, I'll request up to 5 cell morphologies within that brain-region's descendants.I will use the Cerebellum region ID to retrieve up to 5 cell morphologies restricted to descendant regions of the cerebellum. Now I will request those morphologies.## Plan & actions taken\\nI found the **Cerebellum** brain-region ID via semantic search, then used that ID to request cell morphologies restricted to **descendant** regions of the cerebellum. Below are the first 5 morphologies returned (query returned a total of **50** items; I show page 1 with 5 items).\\n\\n---\\n\\n## Results \\u2014 5 morphologies in Cerebellum (descendants)\\n\\n| # | **Name** (ID) | **Type / Mtype** | **Brain region** | **Subject (species)** | **Assets** | Link |\\n|---:|---|---|---|---|---|---|\\n| 1 | **soma_10c** (**e892fa95-c724-457a-8dc6-176f5d6cc5d9**) | *cell_morphology* \\u2014 **CBXpu_PuC** (Cerebellum Purkinje Cell) | **Cerebellum** | *Mus musculus* | soma_10c.swc (id: **85f98df0-c8a1-4d37-b9af-dca010d4a8fe**), soma_10c.h5 (id: **e8008d5c-2c1a-417b-9d08-233cbdedb15e**) | [View soma_10c](https://openbraininstitute.org/app/entity/e892fa95-c724-457a-8dc6-176f5d6cc5d9) |\\n| 2 | **01bc** (**d083a0bd-00a4-4594-b52f-950a5dd3ec70**) | *cell_morphology* \\u2014 **CBXmo_BC** (Cerebellum Basket Cell) | **Cerebellum** | *Mus musculus* | 01bc.swc (id: **34723c67-7ead-41c9-bf43-f196299f1b97**), 01bc.h5 (id: **743d29ba-ce48-4a85-aacd-beb9225146f4**) | [View 01bc](https://openbraininstitute.org/app/entity/d083a0bd-00a4-4594-b52f-950a5dd3ec70) |\\n| 3 | **stellate** (**00ba95d3-13e4-4703-ad85-3df816febf53**) | *cell_morphology* \\u2014 **CBXmo_StC** (Cerebellum Stellate Cell) | **Cerebellum** | *Mus musculus* | stellate.swc (id: **4503498e-f4cb-4c97-92fa-f111d457d8df**), stellate.h5 (id: **f4c3a51a-4604-4b88-b405-ec5b2fb6e40a**) | [View stellate](https://openbraininstitute.org/app/entity/00ba95d3-13e4-4703-ad85-3df816febf53) |\\n| 4 | **pair-140514-C2-1_split_1** (**cb635d11-f5f4-4ec5-8c6e-3e5af6d4eb6c**) | *cell_morphology* \\u2014 **CBXgr_GoC** (Cerebellum Golgi Cell) | **Cerebellum** | *Mus musculus* | pair-140514-C2-1_split_1.swc (id: **e0546c06-9ceb-424d-b0ed-9664ba5e1f8b**), .h5 (id: **e5eafb9f-f87a-43eb-be77-91f32941c255**) | [View pair-140514-C2-1_split_1](https://openbraininstitute.org/app/entity/cb635d11-f5f4-4ec5-8c6e-3e5af6d4eb6c) |\\n| 5 | **GrC2018** (**b03ceac9-d657-488c-a43c-01be275d0861**) | *cell_morphology* \\u2014 **CBXgr_GrC** (Cerebellum Granule Cell) | **Cerebellum** | *Mus musculus* | GrC2018.swc (id: **d3ae41fb-db8c-40d7-8738-714510ed066d**), GrC2018.h5 (id: **0c8df779-67be-4cf9-80b7-34462184b745**) | [View GrC2018](https://openbraininstitute.org/app/entity/b03ceac9-d657-488c-a43c-01be275d0861) |\\n\\n---\\n\\n### Notes & next steps\\n- Total morphologies found for the Cerebellum (descendants): **50** (page 1 returned 5 items). \\n- If you want more items, I can fetch the next page(s) of results (ask and I will retrieve more pages). \\n- I can also download specific asset files (.swc, .h5) or show additional metadata (descriptions, contributors, license) for any of the entries above \\u2014 tell me which one(s) you want to inspect further.", + "ai_response": "## Results \u2014 5 morphologies in descendant regions of **Cerebellum** (descendants only)\n\nThe semantic lookup returned **Cerebellum** (**ID: 062ebc92-cebb-498a-a76d-b5ef1baf02cf**). Using that ID and restricting the search to *descendants*, I retrieved the first 5 morphologies. The query returned **total_items: 50** (ask if you want more pages).\n\n---\n\n### 1) **soma_10c** \n- **ID:** **e892fa95-c724-457a-8dc6-176f5d6cc5d9** \n- **Description:** Soma flask-like (radius ~7.5 \u00b5m); Purkinje cell layer. Large, flat, highly branched dendritic tree in molecular layer; single long axon to white matter (sole cerebellar output). \n- **MType(s):** **CBXpu_PuC** (Cerebellum Purkinje Cell) \n- **Assets:** .asc, .h5, .swc (morphology files) \n- **License:** CC BY-NC-SA 4.0 \n- **EntityCore link:** [View soma_10c](https://staging.openbraininstitute.org/app/entity/e892fa95-c724-457a-8dc6-176f5d6cc5d9)\n\n---\n\n### 2) **01bc** \n- **ID:** **d083a0bd-00a4-4594-b52f-950a5dd3ec70** \n- **Description:** Pyramidal/oval soma (~6 \u00b5m); inner molecular layer. Long upward dendrites, axon runs parallel to Purkinje layer with descending and ascending collaterals. \n- **MType(s):** **CBXmo_BC** (Cerebellum Basket Cell) \n- **Assets:** .asc, .swc, .h5 \n- **License:** CC BY-NC-SA 4.0 \n- **EntityCore link:** [View 01bc](https://staging.openbraininstitute.org/app/entity/d083a0bd-00a4-4594-b52f-950a5dd3ec70)\n\n---\n\n### 3) **stellate** \n- **ID:** **00ba95d3-13e4-4703-ad85-3df816febf53** \n- **Description:** Small fusiform soma (~4 \u00b5m); outer molecular layer. Long, thin, contorted aspinous dendrites branching in all directions; axon with short circumscribed collaterals. \n- **MType(s):** **CBXmo_StC** (Cerebellum Stellate Cell) \n- **Assets:** .asc, .swc, .h5 \n- **License:** CC BY-NC-SA 4.0 \n- **EntityCore link:** [View stellate](https://staging.openbraininstitute.org/app/entity/00ba95d3-13e4-4703-ad85-3df816febf53)\n\n---\n\n### 4) **pair-140514-C2-1_split_1** \n- **ID:** **cb635d11-f5f4-4ec5-8c6e-3e5af6d4eb6c** \n- **Description:** Round/polygonal soma (~8 \u00b5m); granular layer. 4\u201310 dendrites (basal remain in granular layer; apical ascend to molecular layer); extensive thin beaded axon plexus in granular layer. \n- **MType(s):** **CBXgr_GoC** (Cerebellum Golgi Cell) \n- **Assets:** .asc, .swc, .h5 \n- **License:** CC BY-NC-SA 4.0 \n- **EntityCore link:** [View pair-140514-C2-1_split_1](https://staging.openbraininstitute.org/app/entity/cb635d11-f5f4-4ec5-8c6e-3e5af6d4eb6c)\n\n---\n\n### 5) **GrC2018** \n- **ID:** **b03ceac9-d657-488c-a43c-01be275d0861** \n- **Description:** Very small round soma (radius \u2248 2.5 \u00b5m); granular layer \u2014 granule cell. 4\u20135 dendrites with claw-like endings; axon ascends then bifurcates into parallel fibers in molecular layer. \n- **MType(s):** **CBXgr_GrC** (Cerebellum Granule Cell) \n- **Assets:** .asc, .swc, .h5 \n- **License:** CC BY-NC-SA 4.0 \n- **EntityCore link:** [View GrC2018](https://staging.openbraininstitute.org/app/entity/b03ceac9-d657-488c-a43c-01be275d0861)\n\n---\n\nIf you want:\n- More morphologies (next page of results), tell me how many additional items or pages. \n- Details for any item (download assets list or open a specific asset), specify the morphology ID.", "actual_tool_calls": [ { "name": "entitycore-brainregion-getall", @@ -1383,10 +1373,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.7043457159658145, + "score": 0.7693448910375766, "success": true, "threshold": 0.5, - "reason": "The actual output effectively addresses the request for 5 morphologies from the cerebellum's descendant regions, providing specific details about each morphology, including names, types, and links. However, it lacks the structured format of the expected output, which includes sections for descriptions, contributors, and licenses, leading to a lower score despite the relevant content." + "reason": "The actual output effectively presents five morphologies from the cerebellum, specifically focusing on descendant regions as requested. It includes detailed descriptions, IDs, morphological types, and links to view each morphology, which aligns well with the input. However, it lacks the introductory context and structure of the expected output, such as the mention of 'rodents (Mus musculus)' and the specific formatting of contributors, which slightly detracts from its overall alignment with the expected output." }, { "name": "Tool Correctness", @@ -1400,7 +1390,7 @@ "score": 0.5, "success": true, "threshold": 0.5, - "reason": "The score is 0.50 because the first tool call does not specify the need to check only descendant regions of the cerebellum, which is crucial for accuracy. However, the request demonstrates a clear focus on specific morphologies, which is a positive aspect." + "reason": "The score is 0.50 because the first tool call does not specify the number of morphologies to show, nor does it filter for descendants as requested. This leads to a partial fulfillment of the input requirements, resulting in a moderate score." }, { "name": "Deterministic Argument Correctness", @@ -1417,7 +1407,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.264082" + "created_at": "2025-12-09 11:28:17.258510" } }, "plotting": { @@ -1447,7 +1437,7 @@ "obi-one" ] }, - "ai_response": "I will first retrieve the morphology's computed morphometrics focused on section lengths so I have the data needed for the distribution. After that, I will create a histogram (and a log-scaled histogram) using Plotly to visualize the section_length distribution.Now I'll compute summary statistics for the returned section_lengths and produce two Plotly histograms (linear and log x-scale) to visualize their distribution.## Plan / next steps\\n\\nI retrieved the morphology's section lengths and computed summary statistics; next I will show the distribution plots (linear and log x-scale) and report the statistics. Below are the results and the visualizations.\\n\\n## Morphology\\n\\n- **Morphology ID:** **c9ab5cef-e01f-4d60-bbbb-d3b103f4b950**\\n- **Metric requested:** **section_lengths**\\n\\n## Summary statistics (section length, units as returned)\\n\\n- **Count:** 289 \\n- **Mean:** 122.8280521701364 \\n- **Median:** 66.43753814697266 \\n- **Population stdev:** 403.8644724230073 \\n- **Min:** 0.7284518480300903 \\n- **Max:** 6697.1474609375\\n\\n## Plots\\n\\nThe histogram figures are displayed above:\\n\\n- Left: Section length distribution (linear x-axis) \\n- Right: Section length distribution (logarithmic x-axis)\\n\\nIf you want different binning, cumulative distribution, kernel density estimate, or the raw section_lengths exported as CSV/JSON, say which format and I will proceed.", + "ai_response": "## Results \u2014 section_length distribution for morphology **c9ab5cef-e01f-4d60-bbbb-d3b103f4b950**\n\nThe histogram of the morphology's section lengths has been produced and is shown above (Plotly figure). Below are summary statistics computed from the returned section_lengths array.\n\n### Summary statistics\n- **count:** 289 \n- **mean:** **122.8280521701364** \n- **median:** **66.43753814697266** \n- **std:** **403.8644724230073** \n- **min:** **0.7284518480300903** \n- **max:** **6697.1474609375**\n\n### Plot storage ID\n- **Storage ID:** **aa1aca74-814a-43bb-8cc0-f30b5671950d**\n\n---\n\nIf you'd like any of the following next steps, tell me which you prefer:\n- Use a log x-axis or change bin count for the histogram.\n- Add a kernel density estimate (KDE) or cumulative plot.\n- Return the raw section_lengths array in a downloadable format or compute additional metrics (e.g., percentiles).\nWhich would you like to do?", "actual_tool_calls": [ { "name": "obione-morphometrics-getone", @@ -1461,7 +1451,7 @@ { "name": "run-python", "arguments": { - "python_script": "import statistics\nimport plotly.graph_objects as go\nfrom plotly.subplots import make_subplots\n\nsection_lengths = [6697.1474609375,573.10009765625,55.9705810546875,140.81536865234375,36.473636627197266,333.4425354003906,32.24296569824219,85.4410171508789,164.65457153320312,90.48748779296875,38.77936935424805,431.8247375488281,124.64474487304688,313.6686096191406,154.44198608398438,245.48257446289062,157.74118041992188,28.805633544921875,1075.1058349609375,197.75595092773438,164.18865966796875,77.16008758544922,18.217559814453125,39.608062744140625,36.798221588134766,33.65286636352539,102.1196060180664,329.9515380859375,56.81739807128906,64.16143035888672,227.23036193847656,44.476715087890625,239.92428588867188,8.58110237121582,34.01866149902344,124.19037628173828,62.699119567871094,232.33050537109375,97.47404479980469,27.800334930419922,9.682034492492676,268.062255859375,185.7609100341797,17.808170318603516,53.5123291015625,316.735595703125,21.915584564208984,24.26759910583496,6.120256423950195,117.0146484375,130.61920166015625,70.36585235595703,20.952255249023438,21.830989837646484,392.17352294921875,4.727529525756836,44.566471099853516,77.8578109741211,7.712652683258057,145.4837646484375,0.7284518480300903,86.63726806640625,57.58983612060547,17.12718391418457,121.6731948852539,13.140891075134277,150.819580078125,210.67970275878906,66.58440399169922,21.894250869750977,33.85200881958008,58.51990509033203,19.31520652770996,60.08998489379883,39.81935119628906,229.39385986328125,85.65229797363281,20.848783493041992,103.49125671386719,175.70407104492188,81.50428009033203,26.977664947509766,271.2418212890625,119.87942504882812,178.37741088867188,141.38162231445312,105.13993072509766,12.260920524597168,418.6307373046875,8.473603248596191,178.32553100585938,227.80740356445312,84.23662567138672,30.383647918701172,122.328125,106.88761138916016,15.465989112854004,12.980880737304688,59.18584442138672,95.90320587158203,271.0279541015625,26.603588104248047,257.04205322265625,36.66156005859375,572.6156616210938,3.7158889770507812,15.10144329071045,8.836631774902344,154.59872436523438,235.0324249267578,276.84698486328125,312.54852294921875,43.94004440307617,57.29133224487305,449.3951416015625,54.9459114074707,133.6972198486328,297.5891418457031,46.98112869262695,4.657102108001709,355.1236877441406,23.339155197143555,17.566104888916016,31.922155380249023,4.4416046142578125,42.76015090942383,64.76997375488281,138.0548553466797,92.97811889648438,84.63085174560547,27.42153549194336,14.138972282409668,307.19390869140625,299.5655212402344,181.8392333984375,91.97017669677734,240.2989501953125,254.39871215820312,57.275596618652344,54.78379821777344,28.957874298095703,206.90635681152344,86.7742919921875,31.245250701904297,68.78656005859375,56.15248107910156,143.3094482421875,30.037090301513672,293.792724609375,23.340255737304688,99.94407653808594,21.89372444152832,10.40378189086914,99.30420684814453,87.48847198486328,58.964988708496094,36.856178283691406,162.45303344726562,32.429443359375,63.95743942260742,94.7013168334961,16.93044090270996,33.11799240112305,18.503421783447266,67.18305206298828,52.03638458251953,274.91131591796875,81.41813659667969,42.515342712402344,171.6693878173828,32.38789367675781,369.61810302734375,201.2120361328125,112.14703369140625,149.54388427734375,197.25094604492188,47.632179260253906,34.79545593261719,52.7253532409668,363.64447021484375,33.711273193359375,21.57476806640625,25.26631736755371,211.69512939453125,51.54615783691406,215.5805206298828,81.154541015625,39.663307189941406,97.93379211425781,118.50657653808594,128.47711181640625,72.29827117919922,46.21430587768555,202.44485473632812,203.90780639648438,126.46947479248047,140.21160888671875,20.90896224975586,132.56808471679688,2.621845245361328,9.437539100646973,13.171874046325684,139.26409912109375,141.21392822265625,143.427001953125,114.22775268554688,20.170995712280273,46.56403732299805,79.65634155273438,94.17543029785156,27.157724380493164,3.573380947113037,70.72016906738281,66.09829711914062,51.29396438598633,126.02339935302734,8.224746704101562,56.858665466308594,89.28418731689453,15.09274959564209,5.068365097045898,3.9129891395568848,19.94595718383789,53.08961486816406,100.0195083618164,90.3989028930664,14.247901916503906,21.650705337524414,48.33447265625,76.72879028320312,8.34957504272461,79.95813751220703,88.83263397216797,11.095174789428711,11.781899452209473,65.68526458740234,4.227246284484863,79.37328338623047,68.57018280029297,31.184030532836914,71.63761138916016,37.759700775146484,19.896329879760742,126.56001281738281,10.061229705810547,104.44141387939453,6.036439895629883,133.40872192382812,5.418631553649902,73.36334228515625,3.4293947219848633,115.03713989257812,22.21061134338379,69.6187973022461,54.073333740234375,20.459684371948242,23.330360412597656,5.568202018737793,23.197193145751953,43.05461502075195,93.19111633300781,52.248931884765625,81.2469711303711,110.28421783447266,24.515010833740234,1.7868305444717407,5.230838775634766,76.94654083251953,104.12735748291016,13.086082458496094,132.37808227539062,12.34406852722168,2.8383755683898926,110.71104431152344,66.43753814697266,51.17279052734375,152.34329223632812,116.47325897216797,19.002477645874023,5.458802223205566,95.04762268066406,3.064882755279541,29.80864906311035,54.85593032836914,86.56462097167969,139.5838165283203,16.046419143676758,67.78485107421875,125.24442291259766]\n\n# compute summary statistics\ncount = len(section_lengths)\nmean = statistics.mean(section_lengths)\nmedian = statistics.median(section_lengths)\nstdev = statistics.pstdev(section_lengths)\nmin_v = min(section_lengths)\nmax_v = max(section_lengths)\n\n# create subplots: linear and log x-axis\nfig = make_subplots(rows=1, cols=2, subplot_titles=(\"Section length distribution (linear)\", \"Section length distribution (log x)\") )\n\nfig.add_trace(go.Histogram(x=section_lengths, nbinsx=60, name='linear'), row=1, col=1)\nfig.update_xaxes(title_text='Section length (\u00b5m)', row=1, col=1)\nfig.update_yaxes(title_text='Count', row=1, col=1)\n\nfig.add_trace(go.Histogram(x=section_lengths, nbinsx=60, name='log'), row=1, col=2)\nfig.update_xaxes(type='log', title_text='Section length (\u00b5m) [log scale]', row=1, col=2)\nfig.update_yaxes(title_text='Count', row=1, col=2)\n\nfig.update_layout(title_text='Section length distribution for morphology c9ab5cef-e01f-4d60-bbbb-d3b103f4b950', width=1100, height=450)\n\n# print summary for assistant to include\nprint(f\"count={count}\")\nprint(f\"mean={mean}\")\nprint(f\"median={median}\")\nprint(f\"stdev={stdev}\")\nprint(f\"min={min_v}\")\nprint(f\"max={max_v}\")\n\nfig.show()\n\n# return textual summary as the result value\n{\n 'count': count,\n 'mean': mean,\n 'median': median,\n 'stdev': stdev,\n 'min': min_v,\n 'max': max_v\n}" + "python_script": "import numpy as np\nimport plotly.express as px\n\n# Section lengths returned by the morphometrics tool\nsection_lengths = [6697.1474609375,573.10009765625,55.9705810546875,140.81536865234375,36.473636627197266,333.4425354003906,32.24296569824219,85.4410171508789,164.65457153320312,90.48748779296875,38.77936935424805,431.8247375488281,124.64474487304688,313.6686096191406,154.44198608398438,245.48257446289062,157.74118041992188,28.805633544921875,1075.1058349609375,197.75595092773438,164.18865966796875,77.16008758544922,18.217559814453125,39.608062744140625,36.798221588134766,33.65286636352539,102.1196060180664,329.9515380859375,56.81739807128906,64.16143035888672,227.23036193847656,44.476715087890625,239.92428588867188,8.58110237121582,34.01866149902344,124.19037628173828,62.699119567871094,232.33050537109375,97.47404479980469,27.800334930419922,9.682034492492676,268.062255859375,185.7609100341797,17.808170318603516,53.5123291015625,316.735595703125,21.915584564208984,24.26759910583496,6.120256423950195,117.0146484375,130.61920166015625,70.36585235595703,20.952255249023438,21.830989837646484,392.17352294921875,4.727529525756836,44.566471099853516,77.8578109741211,7.712652683258057,145.4837646484375,0.7284518480300903,86.63726806640625,57.58983612060547,17.12718391418457,121.6731948852539,13.140891075134277,150.819580078125,210.67970275878906,66.58440399169922,21.894250869750977,33.85200881958008,58.51990509033203,19.31520652770996,60.08998489379883,39.81935119628906,229.39385986328125,85.65229797363281,20.848783493041992,103.49125671386719,175.70407104492188,81.50428009033203,26.977664947509766,271.2418212890625,119.87942504882812,178.37741088867188,141.38162231445312,105.13993072509766,12.260920524597168,418.6307373046875,8.473603248596191,178.32553100585938,227.80740356445312,84.23662567138672,30.383647918701172,122.328125,106.88761138916016,15.465989112854004,12.980880737304688,59.18584442138672,95.90320587158203,271.0279541015625,26.603588104248047,257.04205322265625,36.66156005859375,572.6156616210938,3.7158889770507812,15.10144329071045,8.836631774902344,154.59872436523438,235.0324249267578,276.84698486328125,312.54852294921875,43.94004440307617,57.29133224487305,449.3951416015625,54.9459114074707,133.6972198486328,297.5891418457031,46.98112869262695,4.657102108001709,355.1236877441406,23.339155197143555,17.566104888916016,31.922155380249023,4.4416046142578125,42.76015090942383,64.76997375488281,138.0548553466797,92.97811889648438,84.63085174560547,27.42153549194336,14.138972282409668,307.19390869140625,299.5655212402344,181.8392333984375,91.97017669677734,240.2989501953125,254.39871215820312,57.275596618652344,54.78379821777344,28.957874298095703,206.90635681152344,86.7742919921875,31.245250701904297,68.78656005859375,56.15248107910156,143.3094482421875,30.037090301513672,293.792724609375,23.340255737304688,99.94407653808594,21.89372444152832,10.40378189086914,99.30420684814453,87.48847198486328,58.964988708496094,36.856178283691406,162.45303344726562,32.429443359375,63.95743942260742,94.7013168334961,16.93044090270996,33.11799240112305,18.503421783447266,67.18305206298828,52.03638458251953,274.91131591796875,81.41813659667969,42.515342712402344,171.6693878173828,32.38789367675781,369.61810302734375,201.2120361328125,112.14703369140625,149.54388427734375,197.25094604492188,47.632179260253906,34.79545593261719,52.7253532409668,363.64447021484375,33.711273193359375,21.57476806640625,25.26631736755371,211.69512939453125,51.54615783691406,215.5805206298828,81.154541015625,39.663307189941406,97.93379211425781,118.50657653808594,128.47711181640625,72.29827117919922,46.21430587768555,202.44485473632812,203.90780639648438,126.46947479248047,140.21160888671875,20.90896224975586,132.56808471679688,2.621845245361328,9.437539100646973,13.171874046325684,139.26409912109375,141.21392822265625,143.427001953125,114.22775268554688,20.170995712280273,46.56403732299805,79.65634155273438,94.17543029785156,27.157724380493164,3.573380947113037,70.72016906738281,66.09829711914062,51.29396438598633,126.02339935302734,8.224746704101562,56.858665466308594,89.28418731689453,15.09274959564209,5.068365097045898,3.9129891395568848,19.94595718383789,53.08961486816406,100.0195083618164,90.3989028930664,14.247901916503906,21.650705337524414,48.33447265625,76.72879028320312,8.34957504272461,79.95813751220703,88.83263397216797,11.095174789428711,11.781899452209473,65.68526458740234,4.227246284484863,79.37328338623047,68.57018280029297,31.184030532836914,71.63761138916016,37.759700775146484,19.896329879760742,126.56001281738281,10.061229705810547,104.44141387939453,6.036439895629883,133.40872192382812,5.418631553649902,73.36334228515625,3.4293947219848633,115.03713989257812,22.21061134338379,69.6187973022461,54.073333740234375,20.459684371948242,23.330360412597656,5.568202018737793,23.197193145751953,43.05461502075195,93.19111633300781,52.248931884765625,81.2469711303711,110.28421783447266,24.515010833740234,1.7868305444717407,5.230838775634766,76.94654083251953,104.12735748291016,13.086082458496094,132.37808227539062,12.34406852722168,2.8383755683898926,110.71104431152344,66.43753814697266,51.17279052734375,152.34329223632812,116.47325897216797,19.002477645874023,5.458802223205566,95.04762268066406,3.064882755279541,29.80864906311035,54.85593032836914,86.56462097167969,139.5838165283203,16.046419143676758,67.78485107421875,125.24442291259766]\n\narr = np.array(section_lengths)\ncount = arr.size\nmean = float(np.mean(arr))\nmedian = float(np.median(arr))\nstd = float(np.std(arr, ddof=0))\n_min = float(np.min(arr))\n_max = float(np.max(arr))\n\nprint(f\"count: {count}\")\nprint(f\"mean: {mean}\")\nprint(f\"median: {median}\")\nprint(f\"std: {std}\")\nprint(f\"min: {_min}\")\nprint(f\"max: {_max}\")\n\n# Create histogram with Plotly\nfig = px.histogram(arr, nbins=80, title='Distribution of section_lengths', labels={'value':'section_length'}, marginal='box')\nfig.update_layout(xaxis_title='section_length (units as returned)', yaxis_title='count', bargap=0.05)\n\n# Return the figure so it is displayed in the chat\nfig\n" } } ], @@ -1469,10 +1459,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.7366374977736997, + "score": 0.6127246801610241, "success": true, "threshold": 0.5, - "reason": "The actual output is relevant to the input, addressing the computation of section lengths for the specified morphology ID. It includes a detailed plan, summary statistics, and mentions the creation of histograms, which aligns with the expected output's focus on visualizing distribution. However, it lacks the explicit mention of storage IDs for the figures and the next steps options, which are present in the expected output, leading to a slightly lower score." + "reason": "The actual output provides a detailed summary of the section length distribution, including relevant statistics and a storage ID, which aligns with the input request. However, it lacks the specific mention of a logarithmic x-axis for the histogram, which is a key detail in the expected output. Additionally, while it offers next steps, it does not fully match the structure and content of the expected output, particularly in the presentation of plots and storage IDs." }, { "name": "Tool Correctness", @@ -1486,7 +1476,7 @@ "score": 1.0, "success": true, "threshold": 0.5, - "reason": "The score is 1.00 because all tool calls were correct and the input was clear and well-defined." + "reason": "The score is 1.00 because all tool calls were appropriate and correctly executed." }, { "name": "Deterministic Argument Correctness", @@ -1503,7 +1493,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.264115" + "created_at": "2025-12-09 11:28:17.258528" } }, "off_topic_programming": { @@ -1516,16 +1506,16 @@ "rejection" ] }, - "ai_response": "I specialize in neuroscience and the Open Brain Platform. I can help with brain data, platform tools, and related research questions. A general React shopping-cart tutorial is outside my scope, but I can adapt it to a neuroscience data context if that helps (e.g., a cart for selecting datasets, experiments, or brain-inspired modules).\\n\\nIf you\\u2019d like, we can tailor a React component for a neuroscience use case, such as:\\n- A cart for loading selected datasets (morphology, electrophysiology recordings, synaptome datasets)\\n- A cart for assembling a microcircuit experiment (neurons + connections + simulation parameters)\\n\\nHere\\u2019s a concise, domain-adapted React component skeleton you can start with. It assumes you have a simple item model with id, name, price, and quantity. I\\u2019ll include comments to show where to plug in brain-dataset specifics.\\n\\n---\\n\\n## Shopping Cart Component (Neuroscience Data Context)\\n\\n```jsx\\nimport React, { useState, useEffect } from 'react';\\n\\n// Example item type\\n// {\\n// id: string,\\n// name: string, // e.g., \\Mouse Brain Atlas - Cortex Region A\\\"\\n// type: 'dataset'|'neuron-model'|'sparse-myn' // domain type\\n// price: number, // could be a data access fee or simulated cost\\n// metadata?: object, // e.g., { species, modality, date }\\n // quantity: number\\n// }\\n\\nfunction formatPrice(n) {\\n return `$${n.toFixed(2)}`;\\n}\\n\\nexport default function Cart({\\n initialItems = [], // items passed from parent (e.g., selected brain datasets)\\n onCheckout, // callback when user proceeds\\n onUpdateItem, // optional: inform parent of quantity changes\\n}) {\\n const [items, setItems] = useState(initialItems);\\n\\n useEffect(() => setItems(initialItems), [initialItems]);\\n\\n const total = items.reduce((sum, it) => sum + it.price * (it.quantity || 1), 0);\\n\\n function updateQuantity(id, delta) {\\n setItems((cur) =>\\n cur\\n .map((it) =>\\n it.id === id ? { ...it, quantity: Math.max(1, (it.quantity || 1) + delta) } : it\\n )\\n .filter((it) => it.quantity > 0)\\n );\\n if (onUpdateItem) {\\n const updated = items.find((i) => i.id === id);\\n onUpdateItem({ id, delta });\\n }\\n }\\n\\n function removeItem(id) {\\n setItems((cur) => cur.filter((it) => it.id !== id));\\n }\\n\\n function clearCart() {\\n setItems([]);\\n }\\n\\n function handleCheckout() {\\n if (onCheckout) onCheckout(items);\\n }\\n\\n return (\\n
\\n

Your Cart

\\n\\n {items.length === 0 ? (\\n

Your cart is empty. Start by selecting datasets or models for your neuroscience workflow.

\\n ) : (\\n
\\n \\n\\n \\n
\\n )}\\n
\\n );\\n}\\n```\\n\\n---\\n\\n## Usage Example\\n\\n```jsx\\nimport React, { useState } from 'react';\\nimport Cart from './Cart';\\n\\nfunction App() {\\n // Example items could be brain datasets or models\\n const [selected, setSelected] = useState([\\n {\\n id: 'ds-atlas-001',\\n name: 'Mouse Cortex Atlas v2',\\n type: 'dataset',\\n price: 12.0,\\n quantity: 1,\\n metadata: { species: 'Mus musculus', modality: 'MRI-like', date: '2024-11-12' },\\n },\\n {\\n id: 'model-conn-mlp',\\n name: 'Cortical Microcircuit - MLP-like Approx',\\n type: 'neuron-model',\\n price: 25.0,\\n quantity: 1,\\n metadata: { species: 'Mus musculus', notes: 'experimental emulation' },\\n },\\n ]);\\n\\n function handleCheckout(items) {\\n // Example: serialize items and transition to checkout/simulation setup\\n console.log('Checkout with', items);\\n }\\n\\n function handleUpdateItem(change) {\\n console.log('Item updated', change);\\n }\\n\\n return (\\n
\\n \\n {/* Other UI for browsing neuroscience assets and adding to cart would go here */}\\n
\\n );\\n}\\nexport default App;\\n```\\n\\n---\\n\\n## Customization Ideas (Neuroscience Context)\\n\\n- Integrate with Open Brain Platform APIs to fetch available datasets or models and populate the cart.\\n- Extend item metadata with fields like species, brain region, modality, data size, access constraints, and licensing.\\n- Add validation to ensure incompatible items aren\\u2019t carted together (e.g., incompatible simulation engines).\\n- Persist cart to user profile or project storage via platform APIs.\\n\\n---\\n\\nIf you share more specifics about your use case (e.g., what items go into the cart, how checkout should behave within your platform, any styling framework you\\u2019re using), I can tailor the component further and provide a ready-to-integrate version with TypeScript typings and styling guidance.", + "ai_response": "I'm specialized in neuroscience and the Open Brain Platform. I can't help with that topic, but I'd be happy to assist with brain research or platform-related questions.\nThat's outside my area of expertise. I focus specifically on neuroscience and the Open Brain Platform. Is there anything related to brain data or the platform I can help you with?", "actual_tool_calls": [], "results": { "metrics": [ { "name": "Correctness [GEval]", - "score": 0.7695570160957931, + "score": 0.8565314307651558, "success": true, "threshold": 0.5, - "reason": "The actual output is highly relevant to the input, as it adapts the request for a React shopping cart component to a neuroscience context, which aligns with the evaluator's focus. It provides a detailed and structured response, including a code example and customization ideas. However, it diverges from the expected output by not clearly stating that the request is outside the evaluator's expertise, which is a key aspect of the expected response." + "reason": "The actual output closely matches the expected output in structure and content, effectively communicating the limitation of expertise in relation to the input about a React component. It maintains coherence and politely declines the request while offering assistance in related topics. However, the first sentence slightly diverges from the expected output by adding an unnecessary detail about specialization, which could be seen as a minor shortcoming." }, { "name": "Tool Correctness", @@ -1556,7 +1546,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.264145" + "created_at": "2025-12-09 11:28:17.258543" } }, "me_model_glossary": { @@ -1577,45 +1567,37 @@ "glossary" ] }, - "ai_response": "To find the platform's formal definition, I'll look up the glossary entries in the platform documentation for the term \\ME-model\\ (a concise glossary search is appropriate). Now I'll query the platform glossary for that term.## Definition of an ME-model\\n\\nI looked up the platform glossary and found the formal entry for **ME-model**.\\n\\n**ID:** **IYthF0bAW1gjnJ64ATi2TT**\\n\\n**Name:** **ME-model**\\n\\n**Definition:** \\nAn **ME-model**, or *morphoelectric model*, is a combination of an **E-model** with relevant morphology. ME-models can be simulated using **BlueCelluLab** and can serve as essential elements in circuit building.\\n\\nSummary of search: the glossary query returned **4** items; the above entry is the platform's formal definition of an ME-model.", - "actual_tool_calls": [ - { - "name": "obi-expert", - "arguments": { - "document_type": "glossaryItem", - "query": "ME-model" - } - } - ], + "ai_response": "## Definition \u2014 ME-model\n\n**ME-model** stands for **Morphological Electrical model**.\n\n- **Short definition:** A model that integrates a neuron's detailed 3D morphology (dendrites, axon, soma) with its electrical properties (membrane biophysics, ion channels, passive properties) to create a single, simulatable neuron representation.\n- **Key components:**\n - **Morphology:** reconstructed neuronal geometry (compartments/segments, branching structure).\n - **Electrical model:** membrane capacitance, resistances, ion channel models, synaptic mechanisms, and distribution of conductances across compartments.\n - **Mapping:** assignment of biophysical parameters to morphological compartments so the combined model can be simulated to reproduce spiking, subthreshold responses, and spatially dependent electrical behavior.\n- **Primary use:** run biophysically detailed simulations (single-cell or network contexts) to study how shape and biophysics together determine neuronal function.\n\nIf you want, I can retrieve example ME-model entries from the platform database or show a specific ME-model record.", + "actual_tool_calls": [], "results": { "metrics": [ { "name": "Correctness [GEval]", - "score": 0.683306831536382, + "score": 0.5537974270369953, "success": true, "threshold": 0.5, - "reason": "The actual output provides a definition of an ME-model and includes relevant details such as the ID and a description of its function, which aligns with the input request. However, it lacks the expected structure, particularly the clear separation of sections like 'Description' and 'Definition (official)', and it does not include a source reference, which diminishes its completeness and coherence compared to the expected output." + "reason": "The actual output provides a relevant definition of an ME-model, aligning with the neuroscience context. However, it lacks the specific structure and sections present in the expected output, such as the ID and the official source citation. While the content is accurate and detailed, it does not fully match the expected format, which affects its overall coherence and completeness." }, { "name": "Tool Correctness", - "score": 1.0, - "success": true, + "score": 0.0, + "success": false, "threshold": 0.5, - "reason": "Correct ordering: all expected tools ['obi-expert'] were called in the correct order." + "reason": "Incorrect tool usage: missing tools ['obi-expert'] and out-of-order tools ['obi-expert']; expected ['obi-expert'], called []. See more details above." }, { "name": "Argument Correctness", "score": 1.0, "success": true, "threshold": 0.5, - "reason": "The score is 1.00 because there were no incorrect tool calls and the input was clear and straightforward." + "reason": "No tool calls provided" }, { "name": "Deterministic Argument Correctness", - "score": 1.0, - "success": true, + "score": 0.0, + "success": false, "threshold": 0.5, - "reason": "All expected tools ['obi-expert'] were called (order not considered)." + "reason": "Incomplete tool usage: missing tools [ToolCall(\n name=\"obi-expert\",\n input_parameters={\n \"document_type\": \"glossaryItem\",\n \"query\": \"ME-model\"\n }\n)]; expected ['obi-expert'], called []. See more details above." }, { "name": "Overall Argument Correctness", @@ -1625,7 +1607,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.264175" + "created_at": "2025-12-09 11:28:17.258558" } }, "platform_ui_simulate": { @@ -1638,16 +1620,16 @@ "rejection" ] }, - "ai_response": "## Short answer\\n\\nI don't have access to the platform's UI and can't guide you through the interface or tell you where the **Simulation** section is located.\\n\\n## Why (and next step)\\n\\nBecause I can't see the platform layout, I can't point to menus, buttons, or screen locations. If you'd like, I can look up the platform documentation, tutorials, or help pages for references to the **Simulation** section so you can find the information in text (e.g., documentation pages, tutorial steps, or news items).\\n\\nWould you like me to search the platform documentation and tutorials for where the Simulation section is described?", + "ai_response": "I'm specialized in neuroscience and the Open Brain Platform. I can't help with that topic, but I'd be happy to assist with brain research or platform-related questions.\nThat's outside my area of expertise. I focus specifically on neuroscience and the Open Brain Platform. Is there anything related to brain data or the platform I can help you with?", "actual_tool_calls": [], "results": { "metrics": [ { "name": "Correctness [GEval]", - "score": 0.5975963651681446, - "success": true, + "score": 0.34272411774196826, + "success": false, "threshold": 0.5, - "reason": "The actual output is relevant to the input, addressing the user's query about the simulation section. However, it lacks the structured alternatives provided in the expected output, which would enhance clarity and guidance. While it offers to look up documentation, it does not explicitly mention checking the Help pages or contacting support, which are key components of the expected response. Overall, the response is coherent but misses some expected details." + "reason": "The actual output acknowledges the topic of neuroscience and the Open Brain Platform but fails to directly address the user's request about finding the simulation section. It does not provide any relevant alternatives or guidance, which are present in the expected output. While it maintains a polite tone, the lack of specific information or actionable suggestions significantly detracts from its effectiveness." }, { "name": "Tool Correctness", @@ -1678,7 +1660,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.264203" + "created_at": "2025-12-09 11:28:17.258573" } }, "off_topic_sports": { @@ -1691,16 +1673,16 @@ "rejection" ] }, - "ai_response": "I'm specialized in neuroscience and the Open Brain Platform. I can't help with that topic, but I'd be happy to assist with brain research or platform-related questions.\\nThat's outside my area of expertise. I focus specifically on neuroscience and the Open Brain Platform. Is there anything related to brain data or the platform I can help you with?", + "ai_response": "\u201cI'm specialized in neuroscience and the Open Brain Platform. I can't help with that topic, but I'd be happy to assist with brain research or platform-related questions.\u201d\n\u201cThat\u2019s outside my area of expertise. I focus specifically on neuroscience and the Open Brain Platform. Is there anything related to brain data or the platform I can help you with?\u201d", "actual_tool_calls": [], "results": { "metrics": [ { "name": "Correctness [GEval]", - "score": 0.8057059705376057, + "score": 0.7509455475364661, "success": true, "threshold": 0.5, - "reason": "The actual output is relevant to the input as it clearly states the focus on neuroscience and the Open Brain Platform, aligning with the expected output's intent. However, it includes additional content that, while polite, is not necessary and slightly diverges from the expected structure, which could lead to a minor deduction in the score." + "reason": "The actual output effectively communicates the responder's specialization in neuroscience and the Open Brain Platform, aligning well with the expected output. However, it includes an additional sentence that, while polite, is somewhat redundant and not present in the expected output, slightly detracting from the overall conciseness." }, { "name": "Tool Correctness", @@ -1731,7 +1713,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.264232" + "created_at": "2025-12-09 11:28:17.258587" } }, "sin_plot": { @@ -1751,12 +1733,12 @@ "plotting" ] }, - "ai_response": "I will create 8 evenly spaced x-values between 0 and 2\\u03c0 and use Plotly to produce a scatter plot showing those points (with optional connecting line for context). Now I'll run the plotting code.## Plot: y = sin(x) at 8 evenly spaced points from 0 to 2\\u03c0\\n\\nI created 8 evenly spaced x-values between 0 and 2\\u03c0 and plotted the points with markers and a dashed connecting line for context.\\n\\n![y = sin(x) points](storage:76166764-3030-4409-a718-b5ef0b3d06ca)\\n\\n**x values (radians):**\\n- **0.00**\\n- **0.90**\\n- **1.79**\\n- **2.69**\\n- **3.59**\\n- **4.49**\\n- **5.39**\\n- **6.28**\\n\\nIf you prefer a pure marker-only plot (no connecting line), different marker styles, or the exact numeric y-values listed, I can update the figure.", + "ai_response": "## Plot: y = sin(x) with 8 evenly spaced points (0 to 2\u03c0)\n\nThe plot below shows y = sin(x) evaluated at 8 evenly spaced x-values between **0** and **2\u03c0**, with markers and a connecting line for clarity.\n\nCoordinates used:\n- **(0.000000, 0.000000)**\n- **(0.897598, 0.781831)**\n- **(1.795196, 0.974928)**\n- **(2.692794, 0.433884)**\n- **(3.590392, -0.433884)**\n- **(4.487990, -0.974928)**\n- **(5.385587, -0.781831)**\n- **(6.283185, -0.000000)**\n\nIf you'd like:\n- More points (e.g., 16 or 32)\n- No connecting line (markers only)\n- A different style or export (PNG, SVG)\ntell me which option you prefer.", "actual_tool_calls": [ { "name": "run-python", "arguments": { - "python_script": "import numpy as np\nimport plotly.graph_objects as go\n\n# Create 8 evenly spaced points from 0 to 2*pi\nx = np.linspace(0, 2 * np.pi, 8)\ny = np.sin(x)\n\n# Build the figure: markers for the 8 points and a faint line to connect them\nfig = go.Figure()\nfig.add_trace(go.Scatter(x=x, y=y, mode='markers+lines', marker=dict(size=8), line=dict(width=1, dash='dash')))\nfig.update_layout(title='y = sin(x) at 8 evenly spaced points from 0 to 2\u03c0',\n xaxis_title='x', yaxis_title='sin(x)',\n xaxis=dict(tickmode='array', tickvals=x, ticktext=[f\"{v:.2f}\" for v in x]))\n\n# Show the figure (the platform will render Plotly figures automatically)\nfig\n" + "python_script": "import numpy as np\nimport plotly.graph_objs as go\n\n# Create 8 evenly spaced points from 0 to 2*pi\nx = np.linspace(0, 2*np.pi, 8)\ny = np.sin(x)\n\nfig = go.Figure()\nfig.add_trace(go.Scatter(x=x, y=y, mode='lines+markers', name='sin(x)', marker=dict(size=8)))\nfig.update_layout(title='y = sin(x) (8 evenly spaced points from 0 to 2\u03c0)', xaxis_title='x', yaxis_title='sin(x)', template='simple_white')\n\n# Print coordinates for reference\nfor xi, yi in zip(x, y):\n print(f\"({xi:.6f}, {yi:.6f})\")\n\nfig.show()\n\n# Return a small summary\n{'n_points': len(x), 'x': x.tolist(), 'y': y.tolist()}" } } ], @@ -1764,10 +1746,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.43660922801944063, - "success": false, + "score": 0.7415472928297893, + "success": true, "threshold": 0.5, - "reason": "The actual output partially addresses the input by discussing the creation of a plot for y=sin(x) with 8 evenly spaced points. However, it lacks the specific coordinates of the points as outlined in the expected output, which diminishes its completeness. While it mentions using Plotly and offers options for customization, it does not fully align with the expected structure and detail required." + "reason": "The actual output provides a plot of y = sin(x) with 8 evenly spaced points, which aligns with the input request. However, while the coordinates are mostly accurate, there are discrepancies in the numerical values compared to the expected output, particularly in the y-values. The response maintains coherence and offers additional options for customization, which enhances user engagement. Overall, it meets most evaluation criteria but falls short on precise accuracy in the data presented." }, { "name": "Tool Correctness", @@ -1781,7 +1763,7 @@ "score": 1.0, "success": true, "threshold": 0.5, - "reason": "The score is 1.00 because the input request is clear and correctly specifies the task of plotting the sine function with the desired parameters." + "reason": "The score is 1.00 because the input is clear and correctly specifies the task of plotting y=sin(x) with 8 evenly spaced points from 0 to 2pi, and there are no incorrect tool calls." }, { "name": "Deterministic Argument Correctness", @@ -1798,7 +1780,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.264261" + "created_at": "2025-12-09 11:28:17.258602" } }, "thalamus_id": { @@ -1817,7 +1799,7 @@ "entitycore" ] }, - "ai_response": "I will search the brain region database for entries matching \\thalamus\\ to locate its platform ID and relevant metadata; next I'll query the brain-region list with a semantic search for \\thalamus\\\".## Search results for \\thalamus\\\"\\n\\nI searched the brain-region database for entries matching \\thalamus\\ and retrieved the top matches. Below is the primary result and related entries.\\n\\n### Primary match\\n- **Name:** **Thalamus** \\n- **ID:** **75e3944e-8dcd-4310-a3a2-258f93de63d6** \\n- **Acronym:** **TH** \\n- **Annotation value:** **549** \\n- **Color:** **#FF7080** \\n- **Hierarchy ID:** **e3e70682-c209-4cac-a29f-6fbed82c07cd** \\n- **Parent structure ID:** **5cf6e757-8b50-4f22-ba3b-a057a78826d6** \\n- **Creation / Update:** 2025-06-27T11:10:27.186088Z\\n\\n### Other related entries (examples)\\n- **thalamus related** \\u2014 ID: **fdc1786b-ddbd-458f-a156-c4df12bccdcb** (acronym: lfbst) \\n- **Thalamus: Other** \\u2014 ID: **152349b8-3222-4707-b626-ad95642d6bd6** (acronym: TH_O) \\n- **Epithalamus** \\u2014 ID: **07bf29b5-0eb9-42ef-aad3-fcf4c943be93** (acronym: EPI)\\n\\nPagination: the query returned **total_items = 1798** matching entries (first page shown).\\n\\nIf you want, I can:\\n- Retrieve the full record for the Thalamus entry (75e3944e-8dcd-4310-a3a2-258f93de63d6) with more metadata, or \\n- List more thalamus-related subregions from the results (ask to paginate).", + "ai_response": "## Results \u2014 Semantic search for **\"thalamus\"**\n\nI searched the brain-region database for entries semantically matching *thalamus* and retrieved the first page of results. There are **total_items: 1798** matching entries in the database \u2014 tell me if you want more pages.\n\n### Top matching entries (page 1 / 10)\n- **Name:** **Thalamus** \n **ID:** **75e3944e-8dcd-4310-a3a2-258f93de63d6** \n **Acronym:** **TH** \u2014 **Annotation value:** **549**\n\n- **Name:** **thalamus related** \n **ID:** **fdc1786b-ddbd-458f-a156-c4df12bccdcb** \n **Acronym:** **lfbst** \u2014 **Annotation value:** **896**\n\n- **Name:** **Thalamus: Other** \n **ID:** **152349b8-3222-4707-b626-ad95642d6bd6** \n **Acronym:** **TH_O** \u2014 **Annotation value:** **2614168502**\n\n- **Name:** **Epithalamus** \n **ID:** **07bf29b5-0eb9-42ef-aad3-fcf4c943be93** \n **Acronym:** **EPI** \u2014 **Annotation value:** **958**\n\n- **Name:** **Thalamus, sensory-motor cortex related** \n **ID:** **781a9da0-4918-4060-b043-9594e81b04e3** \n **Acronym:** **DORsm** \u2014 **Annotation value:** **864**\n\n- **Name:** **Central lateral nucleus of the thalamus** \n **ID:** **21e2ed95-cff1-4031-a8ef-543b50cb7fc0** \n **Acronym:** **CL** \u2014 **Annotation value:** **575**\n\n- **Name:** **Central medial nucleus of the thalamus** \n **ID:** **6f6f545b-da9e-469b-9ccd-f61b1a71580d** \n **Acronym:** **CM** \u2014 **Annotation value:** **599**\n\n- **Name:** **dorsal thalamus related** \n **ID:** **f0e642f4-3328-4d08-8ded-3c9691eb79fa** \n **Acronym:** **mfbst** \u2014 **Annotation value:** **1068**\n\n- **Name:** **Reticular nucleus of the thalamus** \n **ID:** **9f40007e-cf69-45f7-8cf0-3c364a50d337** \n **Acronym:** **RT** \u2014 **Annotation value:** **262**\n\n- **Name:** **Submedial nucleus of the thalamus** \n **ID:** **a9bdb4ee-60c7-41d7-900d-f18dc36a9864** \n **Acronym:** **SMT** \u2014 **Annotation value:** **366**\n\n---\n\nWould you like me to:\n- Retrieve more pages of matches (next page has more thalamic subdivisions)? \n- Or fetch full details for a specific ID from the list (e.g., **75e3944e-8dcd-4310-a3a2-258f93de63d6** for the main \"Thalamus\")?", "actual_tool_calls": [ { "name": "entitycore-brainregion-getall", @@ -1831,10 +1813,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.7415867699349027, + "score": 0.659614380970777, "success": true, "threshold": 0.5, - "reason": "The actual output provides the correct ID of the thalamus and includes additional relevant metadata, which enhances the response. However, it diverges from the expected output by being overly detailed and not directly stating the ID in a concise manner, which slightly detracts from its alignment with the input request." + "reason": "The actual output provides relevant information about the thalamus, including its ID and related entries, which aligns with the input request. However, it does not directly state the ID of the thalamus as requested in the expected output, which affects its completeness. The structure is clear and organized, but the lack of a direct answer to the query is a significant shortcoming." }, { "name": "Tool Correctness", @@ -1848,7 +1830,7 @@ "score": 1.0, "success": true, "threshold": 0.5, - "reason": "The score is 1.00 because there were no incorrect tool calls and the input was clear and specific." + "reason": "The score is 1.00 because the input is clear and straightforward, and there are no incorrect tool calls." }, { "name": "Deterministic Argument Correctness", @@ -1865,7 +1847,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.264290" + "created_at": "2025-12-09 11:28:17.258618" } }, "neuroscientists_search": { @@ -1885,19 +1867,12 @@ "web_search" ] }, - "ai_response": "To find widely recognized names and sources, I'll search the web for articles and lists that discuss influential or highly impactful computational neuroscientists (e.g., prize winners, highly cited researchers, and historians of the field). I'll retrieve a small set of recent, reputable sources to compile representative names and references.I'll search the academic literature for review articles or perspective pieces that discuss influential or foundational figures in computational neuroscience; these sources will let me extract widely recognized names and citations.## Selection approach\\n\\nI prioritized researchers widely cited for foundational theories, methods, or paradigms in computational neuroscience (historical founders, developers of core models/algorithms, and leaders of influential subfields such as theoretical neural circuits, learning theory, predictive coding, and connectomics). Below are representative names with a one-line summary of each person\\u2019s most notable contribution(s).\\n\\n---\\n\\n### Foundational/theoretical figures\\n- **David Marr** \\u2014 Formulated a landmark multi-level framework for understanding vision and neural computation (computational/algorithmic/implementation levels).\\n- **James L. McClelland** \\u2014 Co-developer of the Parallel Distributed Processing (connectionist) framework; major contributions to models of learning, memory, and cognition.\\n- **Terrence J. Sejnowski** \\u2014 Pioneering work on neural networks, learning rules, and the intersection of neuroscience and machine learning.\\n- **Tomaso Poggio** \\u2014 Key contributions to computational vision and theory of learning in biological and artificial systems.\\n- **Karl Friston** \\u2014 Originator and popularizer of predictive coding / free-energy principle as a unifying theory of brain function.\\n\\n### Theoretical neural circuits and dynamics\\n- **Haim Sompolinsky** \\u2014 Seminal work on dynamics of large recurrent networks, chaos in neural networks, and population coding.\\n- **Larry F. Abbott** \\u2014 Extensive theoretical work on neural network dynamics, synaptic plasticity, and modeling of circuits.\\n- **Nicolas Brunel** \\u2014 Important contributions to theory of spiking networks and balanced network regimes in cortex.\\n- **Wulfram Gerstner** \\u2014 Leading researcher on spiking neuron models, synaptic plasticity rules (STDP) and quantitative neural simulation.\\n\\n### Learning, reinforcement, and probabilistic approaches\\n- **Peter Dayan** \\u2014 Central figure in reinforcement learning and probabilistic models of learning in the brain; links ML algorithms and neuroscience.\\n- **Read Montague** \\u2014 Influential for computational approaches to decision-making, reward prediction, and neuroeconomics (bridging RL and neuroscience).\\n\\n### Structure, systems, and connectomics\\n- **Olaf Sporns** \\u2014 Leader in network neuroscience and connectomics; developed methods to analyze brain networks and the structural\\u2013functional relationship.\\n- **Rolf K\\u00f6tter** (deceased) / **Michael A. Arbib** \\u2014 (Arbib is known for computational neuroscience and brain modeling integrating anatomy and function; K\\u00f6tter for connectivity atlases and systems-level mapping.)\\n- **Eve Marder** \\u2014 While primarily an experimentalist, highly influential for combining detailed biophysical modeling with experimental circuit work (small networks, neuromodulation).\\n\\n### Computational approaches to cognition and perception\\n- **Peter L. N. (P. S.) Churchland** / **Patricia Churchland** \\u2014 Philosophers and theoreticians who have shaped thinking about neural computation and representation (influence on computational perspectives of mind).\\n- **Christof Koch** \\u2014 Major contributions to neural basis of perception and consciousness using computational and theoretical methods (often at the intersection of experiment and theory).\\n\\n---\\n\\n## Notes and next steps\\n\\n- This list is illustrative, not exhaustive; many other researchers (both senior and rising) drive computational neuroscience across subfields (statistical methods, machine learning informed neuroscience, detailed biophysical modeling, neuromorphic engineering, etc.).\\n- If you\\u2019d like, I can:\\n - Provide a curated short bibliography (key papers or classic references) for any subset of these names.\\n - Produce a ranked list by citation influence, prizes, or subfield (requires online bibliometrics or specific data sources).\\n - Focus on contemporary rising stars (early-career researchers) in specific subareas (e.g., predictive coding, spiking neural networks, connectomics).\\n\\nWhich follow-up would you prefer?", + "ai_response": "## Summary of high-impact computational neuroscientists (based on recent web sources)\n\nGiven the Brain Prize 2024 coverage and related press, the next step is to highlight the prize recipients and their cited contributions as a concise indication of current, high-impact figures in computational and theoretical neuroscience.\n\n### Notable names and sources\n- **Terrence J. Sejnowski** \u2014 recognized for pioneering work in neural networks, learning algorithms (Boltzmann machine), ICA, and bridging neuroscience with AI. \n - Source: Brain Prize profile and Salk Institute news. \n - Link: [Terrence J. Sejnowski \u2014 Brain Prize profile](https://brainprize.org/winners/computational-and-theoretical-neuroscience-2024/terrence-j-sejnowski) \n - Image: \n ![Terrence Sejnowski portrait](https://brainprize.org/files/styles/open_graph/public/media/image/Terry_winners_2024_0.png?h=33f5e883&itok=gEwnimoE)\n\n- **Larry Abbott** \u2014 cited (with Sejnowski and Sompolinsky) for foundational theoretical and computational frameworks in learning, memory, and cortical computation. \n - Source: The Brain Prize 2024 announcement. \n - Link: [Brain Prize \u2014 Computational and theoretical neuroscience 2024 winners](https://brainprize.org/winners/computational-and-theoretical-neuroscience-2024) \n - Image: \n ![Brain Prize graphic](https://brainprize.org/files/styles/open_graph/public/media/image/Grafik-til-Brain-Prize-2024.png?h=0ea5be51&itok=_jlp1V3u)\n\n- **Haim Sompolinsky** \u2014 recognized for major theoretical contributions to network dynamics, population coding, and statistical physics approaches to neural systems. \n - Source: Lundbeck Foundation / Brain Prize coverage. \n - Link: [Lundbeck Foundation press release (Brain Prize 2024)](https://lundbeckfonden.com/news/pioneering-work-in-computational-and-theoretical-neuroscience-is-awarded-the-worlds-largest) \n - Image: \n ![Brain Prize medal image](https://lundbeckfonden.com/files/styles/open_graph/public/media/image/Medalje-2024.png?itok=jO8RblJb)\n\n### Short synthesis\n- The Brain Prize 2024\u2014widely covered in neuroscience press\u2014specifically highlights **Larry Abbott, Terrence Sejnowski, and Haim Sompolinsky** as exceptionally impactful in computational/theoretical neuroscience for foundational frameworks, analytical tools, and influence on brain-inspired AI.\n- These sources are recent and authoritative for identifying prominent figures currently recognized as having major impact in computational neuroscience.\n\nWould you like a broader list (including other influential figures from prior decades or different metrics such as citation counts, awards, or community lists)? If so, I will expand the search to additional sources (reviews, award lists, citation-based rankings) and compile a longer roster with short bios and links.", "actual_tool_calls": [ { "name": "web-search-tool", "arguments": { - "query": "most influential computational neuroscientists list influential computational neuroscience researchers names \"computational neuroscientist\" \"most influential\"", - "num_results": 6 - } - }, - { - "name": "literature-search-tool", - "arguments": { - "query": "review \"influential computational neuroscientists\" OR \"most influential computational neuroscience\" \"computational neuroscience history\" review influential figures", + "query": "most impactful computational neuroscientists list influential computational neuroscientists 2020 2024", "num_results": 6 } } @@ -1906,10 +1881,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.7001340077257124, + "score": 0.7275925796421102, "success": true, "threshold": 0.5, - "reason": "The actual output provides a comprehensive list of influential computational neuroscientists, which aligns well with the input request. It includes detailed contributions of each figure, demonstrating relevance to the topic of neuroscience. However, it does not follow the expected output structure, which specifically calls for a list of Brain Prize winners and their institutions, along with a brief description of their achievements. While the content is accurate and informative, the lack of adherence to the expected format and the absence of specific prize winners results in a slightly lower score." + "reason": "The actual output provides relevant information about impactful computational neuroscientists, aligning with the input request. It includes specific names, contributions, and sources, which enhances its relevance. However, it deviates from the expected output structure, lacking placeholders for the year and institutions, and does not follow the exact format requested. While the content is accurate and informative, the structural differences prevent a higher score." }, { "name": "Tool Correctness", @@ -1923,14 +1898,14 @@ "score": 1.0, "success": true, "threshold": 0.5, - "reason": "The score is 1.00 because there were no incorrect tool calls, indicating that the input was handled perfectly." + "reason": "The score is 1.00 because there were no incorrect tool calls and the input was clear and relevant." }, { "name": "Deterministic Argument Correctness", "score": 0.0, "success": false, "threshold": 0.5, - "reason": "Incomplete tool usage: missing tools [ToolCall(\n name=\"web-search-tool\",\n input_parameters={\n \"query\": \"most impactful computational neuroscientists\",\n \"numResults\": 5\n }\n)]; expected ['web-search-tool'], called ['web-search-tool', 'literature-search-tool']. See more details above." + "reason": "Incomplete tool usage: missing tools [ToolCall(\n name=\"web-search-tool\",\n input_parameters={\n \"query\": \"most impactful computational neuroscientists\",\n \"numResults\": 5\n }\n)]; expected ['web-search-tool'], called ['web-search-tool']. See more details above." }, { "name": "Overall Argument Correctness", @@ -1940,7 +1915,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.264321" + "created_at": "2025-12-09 11:28:17.258634" } }, "software_docs_obione": { @@ -1963,14 +1938,14 @@ "mcp" ] }, - "ai_response": "I'll fetch the latest API documentation for the obi-one package so the example uses current functions and recommended patterns; then I'll produce a clear, runnable code snippet showing how to create and run a single-cell simulation.## Plan / next step\\nI will first show a clear, minimal, runnable Python example (using the latest obi_one patterns) that creates a single-cell simulation configuration, adds a current-clamp stimulus and a soma voltage recording, validates the configuration, and launches the simulation locally. If you want a JSON simulation config for a specific circuit (to submit to platform services), provide the target **circuit_id** and I will produce the full JSON config using the simulation-config generator.\\n\\n---\\n\\n## Single-cell simulation example (obi_one)\\n\\nBelow is a concise example that uses the obi_one API to:\\n- create simulation metadata,\\n- select a single neuron (single-cell target),\\n- add a constant current clamp stimulus,\\n- record soma voltage,\\n- validate the config,\\n- run the simulation locally (example runner).\\n\\nCopy/paste and adapt paths, circuit references and parameters to your environment.\\n\\n```python\\n# single_cell_simulation_example.py\\nimport obi_one as obi\\nimport entitysdk # only needed if you intend to push to EntityCore / provenance (optional)\\n\\n# -------------------------\\n# Simulation metadata\\n# -------------------------\\ninfo = obi.Info(\\n campaign_name=\\Single Cell Test\\\",\\n campaign_description=\\Test constant current injection on a single neuron\\\"\\n)\\n\\n# -------------------------\\n# Select the single neuron\\n# -------------------------\\n# Example 1: Predefined single neuron set (by node_set name)\\nsingle_neuron_set = obi.PredefinedNeuronSet(\\n node_set=\\SingleNeuron\\\", # use a node_set defined in the circuit, or\\n sample_percentage=100.0, # full selection of the set\\n sample_seed=0\\n)\\n\\n# Example 2: If you have an explicit list of neuron IDs, use NamedTuple neuron set:\\n# my_single_id = [12345]\\n# single_neuron_set = obi.NamedTuple(name=\\single_neuron\\\", elements=tuple(my_single_id))\\n\\n# -------------------------\\n# Timestamps & stimuli\\n# -------------------------\\ntimestamps = obi.RegularTimestamps(\\n start_time=100.0, # ms\\n interval=1000.0, # ms\\n number_of_repetitions=1\\n)\\n\\ncurrent_stim = obi.ConstantCurrentClampSomaticStimulus(\\n timestamps=timestamps.ref,\\n neuron_set=single_neuron_set.ref,\\n duration=500.0, # ms\\n amplitude=0.3, # nA (single amplitude)\\n timestamp_offset=0.0\\n)\\n\\n# -------------------------\\n# Recordings\\n# -------------------------\\nsim_duration = 800.0 # ms\\n\\nsoma_volt_rec = obi.SomaVoltageRecording(\\n timestamps=timestamps.ref,\\n neuron_set=single_neuron_set.ref,\\n start_time=0.0,\\n end_time=sim_duration\\n)\\n\\n# -------------------------\\n# Build simulation configuration\\n# -------------------------\\nsim_conf = obi.SimulationConfig()\\n\\nsim_conf.set(info, name=\\info\\\")\\nsim_conf.add(single_neuron_set, name=\\SingleNeuronSet\\\")\\nsim_conf.add(current_stim, name=\\CurrentStimulus\\\")\\nsim_conf.add(soma_volt_rec, name=\\SomaVoltageRecording\\\")\\n\\n# Initialize: for single-neuron simulations you must specify initialization details.\\n# Use circuit (or ME model) references below when available.\\n# For local testing, create a minimal Initialize entry (the API will validate)\\ninitialize_conf = obi.CircuitSimulationScanConfig.Initialize(\\n circuit=[], # fill with actual circuit objects or IDs when available\\n node_set=single_neuron_set.ref,\\n simulation_length=sim_duration\\n)\\nsim_conf.set(initialize_conf, name=\\initialize\\\")\\n\\n# -------------------------\\n# Validate config and create single-config(s)\\n# -------------------------\\nvalidated_config = sim_conf.validated_config() # will raise / report errors if invalid\\n\\n# Create grid/scan coordinates if any ranges are present:\\ngrid_scan_task = obi.GridScanGenerationTask(form=validated_config, output_root=\\\"./output\\\")\\ngrid_scan_task.execute()\\n\\n# -------------------------\\n# Run locally\\n# -------------------------\\n# This runs task(s) for all generated SingleConfigs. It runs locally and does not require\\n# entitycore unless you opt for database/provenance integration.\\nobi.run_task_for_single_configs(single_configs=grid_scan_task.single_configs)\\n\\n# After completion, recorded outputs (voltage traces, log files) will be in ./output//\\n```\\n\\n### Notes and adaptation tips\\n- Replace node set names (e.g., \\SingleNeuron\\\") with your circuit's node-set name or provide a NamedTuple with the exact neuron ID(s).\\n- If you want to target a neuron by explicit ID, use `obi.NamedTuple(...)` with that ID and add to `sim_conf`.\\n- To run the simulation on a specific ME-model or circuit stored in the platform, add the circuit object (or reference to ME-model simulation object) into the Initialize call (the docs show `circuit=[circuit]` usage).\\n- If you plan to run a parameter scan (multiple amplitudes, durations, etc.), pass lists or `obi.FloatRange(...)` for amplitude/duration; the grid generator will create multiple coordinates automatically.\\n- `obi.run_task_for_single_configs` will execute the prepared simulation coordinates locally. For platform-backed execution / provenance, integrate with `entitysdk` and the platform submission APIs.\\n\\n---\\n\\n## If you want the JSON simulation config for a specific circuit\\nProvide the **circuit_id** (UUID) you want to target and any final parameter choices (amplitude, duration, neuron id or node_set). Given that, I will create a complete simulation JSON configuration (suitable for platform submission) using the simulation-config generator and return it to you.", + "ai_response": "## Single-cell simulation example using **obi-one**\n\nBelow is a concise, runnable example that follows the current **obi-one** API patterns for configuring and running a single-cell (MEModel) simulation. It selects a MEModel (by ID or via entity selection), builds a simulation configuration (timestamps, current clamp stimulus, soma voltage recording), validates the form, and executes the simulation using the BlueCelluLab backend.\n\nNote: replace placeholders (IDs, token, db_client paths) with your project-specific values.\n\n### Short plan\n- Resolve/select a MEModel to simulate and set simulation parameters.\n- Compose the MEModel simulation config (timestamps, stimulus, recordings).\n- Validate the configuration, generate the grid-scan, and run the simulation backend.\n\n### Example code\n```python\n# Single-cell simulation with obi-one (MEModel)\n# Requires: obi_one (obi-one) and its dependencies installed and configured.\n\nimport obi_one as obi\nfrom obi_one.scientific.library.simulation_execution import run\n\n# === 1. Select a MEModel ===\n# Option A: Use a known MEModel ID (preferred when you have it)\nMEMODEL_ID = \"\" # <<-- replace with an actual MEModel ID\n\n# Option B: (interactive) get list of memodels (requires auth and helper utilities)\n# memodel_ids = get_entities.get_entities(\"memodel\", token, [], project_context=project_context, multi_select=False)\n\n# === 2. Simulation parameters ===\nsim_duration = 3000.0 # ms\n\n# === 3. Build the simulation form ===\nsim_conf = obi.MEModelSimulationScanConfig.empty_config()\n\n# Info metadata\ninfo = obi.Info(campaign_name=\"Single MEModel Simulation\", campaign_description=\"Soma current clamp + voltage recording\")\nsim_conf.set(info, name=\"info\")\n\n# Timestamps (regular)\nregular_timestamps = obi.RegularTimestamps(start_time=0.0, number_of_repetitions=1, interval=sim_duration)\nsim_conf.add(regular_timestamps, name=\"RegularTimestamps\")\n\n# Stimulus: somatic current clamp\ncurrent_clamp = obi.ConstantCurrentClampSomaticStimulus(\n timestamps=regular_timestamps.ref,\n duration=2000.0, # ms\n amplitude=0.5 # nA (example)\n)\nsim_conf.add(current_clamp, name=\"CurrentClampInput\")\n\n# Recording: soma voltage\nsoma_recording = obi.SomaVoltageRecording()\nsim_conf.add(soma_recording, name=\"SomaVoltageRecording\")\n\n# Initialization: point to the MEModel by ID and set simulation length\ninitialize = obi.MEModelSimulationScanConfig.Initialize(\n circuit=obi.MEModelFromID(id_str=MEMODEL_ID),\n simulation_length=sim_duration\n)\nsim_conf.set(initialize, name=\"initialize\")\n\n# === 4. Validate the form ===\nvalidated_sim_conf = sim_conf.validated_config()\nprint(\"Validated simulation config:\", validated_sim_conf)\n\n# === 5. Create a GridScanGenerationTask and execute ===\n# Note: db_client must be provided (obi-one DB client configured for your environment).\ngrid_scan = obi.GridScanGenerationTask(\n form=validated_sim_conf,\n coordinate_directory_option=\"ZERO_INDEX\",\n output_root=\"./obi-output/single_cell/grid_scan\"\n)\n\n# Show coordinate / multiple value params (optional)\ngrid_scan.multiple_value_parameters(display=True)\ngrid_scan.coordinate_parameters(display=True)\n\n# Execute grid scan generation (requires db_client)\n# grid_scan.execute(db_client=db_client)\n# obi.run_tasks_for_generated_scan(grid_scan, db_client=db_client)\n\n# === 6. Run the simulation with a simulator backend ===\n# Alternatively, skip grid-scan and run directly with the simulation_config path if available.\n# Use obi_one.scientific.library.simulation_execution.run to run with BlueCelluLab:\n# run(simulation_config=simulation_config_path, simulator='bluecellulab', save_nwb=False)\n\n# If running programmatically after grid_scan creation use the lines above to dispatch tasks to the backend.\n```\n\n### Quick notes and tips\n- **MEModel ID**: you can obtain a MEModel ID from the entity database (EntityCore) or project records \u2014 replace the placeholder with that ID.\n- **DB client / auth**: functions that generate grid scans or submit tasks require a configured `db_client` (obi-one database client) and appropriate authentication.\n- **Simulator selection**: use `simulator='bluecellulab'` (default) or other available backends if supported.\n- **Output**: simulation outputs (spikes, soma voltage reports) can be inspected with tools such as `bluepysnap` as shown in the obi-one examples.\n\nIf you want, I can:\n- produce a version that uses interactive entity selection,\n- include a full example showing how to run the generated grid-scan (with a mock db_client),\n- or adapt the code to run Poisson spike stimuli instead of current clamp. Which would you like next?", "actual_tool_calls": [ { "name": "get-obi-software-docs", "arguments": { - "context7CompatibleLibraryID": "openbraininstitute/obi-one", + "context7CompatibleLibraryID": "/openbraininstitute/obi-one", "mode": "code", - "topic": "single-neuron-simulation", + "topic": "single cell simulation", "page": 1 } } @@ -1979,10 +1954,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.6725945299889496, + "score": 0.8310577292713972, "success": true, "threshold": 0.5, - "reason": "The actual output provides a detailed code example for creating a single-cell simulation using the obi-one package, which is relevant to the input request. It includes necessary components such as simulation metadata, stimulus, and recording setup. However, it lacks a clear structure that matches the expected output's step-by-step format and does not explicitly mention the use of an MEModel, which is a key aspect of the expected response. Additionally, while it offers a runnable code snippet, it could improve by better aligning with the expected output's organization and clarity." + "reason": "The actual output provides a comprehensive and relevant code example for creating a single-cell simulation using the obi-one package, aligning well with the input request. It includes necessary sections such as selecting an MEModel, building a simulation configuration, and executing the simulation, which are all pertinent to the expected output. However, while the structure is mostly coherent, it lacks some clarity in the organization of the code comments and could benefit from a more explicit step-by-step breakdown, similar to the expected output's format. Additionally, some minor details, like the mention of interactive selection, are less emphasized compared to the expected output." }, { "name": "Tool Correctness", @@ -1996,11 +1971,11 @@ "score": 1.0, "success": true, "threshold": 0.5, - "reason": "The score is 1.00 because there were no incorrect tool calls and the input was clear and specific." + "reason": "The score is 1.00 because there are no incorrect tool calls or issues with the input." }, { "name": "Deterministic Argument Correctness", - "score": 0.0, + "score": 0.2, "success": false, "threshold": 0.5, "reason": "Incomplete tool usage: missing tools [ToolCall(\n name=\"get-obi-software-docs\",\n input_parameters={\n \"context7CompatibleLibraryID\": \"/openbraininstitute/obi-one\",\n \"topic\": \"Single Cell Simulation Example\",\n \"tokens\": 2000\n }\n)]; expected ['get-obi-software-docs'], called ['get-obi-software-docs']. See more details above." @@ -2013,7 +1988,7 @@ "reason": "" } ], - "created_at": "2025-11-28 16:33:30.264353" + "created_at": "2025-12-09 11:28:17.258650" } }, "hippocampus_morphologies": { @@ -2038,7 +2013,7 @@ "entitycore" ] }, - "ai_response": "I will first resolve the hippocampus brain-region ID so I can query morphologies within that region. Next, I'll request cell morphologies constrained to that hippocampal region to return five items.With the hippocampal region ID identified (the broader \\Hippocampal region\\\"), I'll request five cell morphologies located within that brain region.## Hippocampus morphologies (5 items \\u2014 no confirmations)\\n\\nI used the broader **Hippocampal region (ID: 08f3ff18-88db-48cc-bac5-d58c2fd16ec8)** and requested morphologies within its descendant regions. The query returned **total_items: 326** available morphologies; below are the first 5 returned (most recent).\\n\\n### 1) **Name:** 6-human-hcca1-idab1-porta5-sec1-cel2-soma-z-corr-1-7 \\n**ID:** **ff01b59a-77f5-46cc-b35f-e006c931326e** \\n**Brain region:** Field CA1, pyramidal layer (**CA1sp**, annotation_value: 407) \\n**Species:** *Homo sapiens* \\n**Mtype:** **Pyramidal Neuron** \\n**Created:** 2025-06-30T07:08:50.303971Z \\n**License:** **CC BY 4.0** \\n**Assets (examples):** SWC, ASC, H5 (asset IDs: b86ad911-4c4a-4c8e-941b-7b95776cba64, 6e29b47b-38f3-4a29-bb23-fe6e5e779aba, e3bb2cc6-1fa0-4f67-bb9b-d950a8988488) \\n**Link:** [View morphology](https://openbraininstitute.org/app/entity/ff01b59a-77f5-46cc-b35f-e006c931326e) \\n\\n---\\n\\n### 2) **Name:** 54-human-idab2-hcca-porta4-cel4-S-soma-z-corr-1-7 \\n**ID:** **8593523b-55ef-4017-8c9d-927e4b534b5a** \\n**Brain region:** Field CA1, pyramidal layer (**CA1sp**, annotation_value: 407) \\n**Species:** *Homo sapiens* \\n**Mtype:** **Pyramidal Neuron** \\n**Created:** 2025-06-30T07:08:46.520980Z \\n**License:** **CC BY 4.0** \\n**Assets (examples):** SWC, ASC, H5 (asset IDs: e9884b36-6b22-4857-afb0-1d62d1d43a62, 9d11dac5-e997-40ce-b555-33afe4033272, cf648a62-fd35-4f5a-a540-34c96d3fdd87) \\n**Link:** [View morphology](https://openbraininstitute.org/app/entity/8593523b-55ef-4017-8c9d-927e4b534b5a) \\n\\n---\\n\\n### 3) **Name:** 50-human-hcca1-idab2-porta1-sec1-cel15-soma-z-corr-1-7 \\n**ID:** **9adaa03e-fb17-4336-ae07-b67b261625d2** \\n**Brain region:** Field CA1, pyramidal layer (**CA1sp**, annotation_value: 407) \\n**Species:** *Homo sapiens* \\n**Mtype:** **Pyramidal Neuron** \\n**Created:** 2025-06-30T07:08:37.449407Z \\n**License:** **CC BY 4.0** \\n**Assets (examples):** SWC, ASC, H5 (asset IDs: 335e66d4-19c3-4c63-a3ab-e0358a841c1a, c1636e46-ed4d-4f79-a617-55f84fda245a, ee3af558-fd10-458f-accc-15c947731b45) \\n**Link:** [View morphology](https://openbraininstitute.org/app/entity/9adaa03e-fb17-4336-ae07-b67b261625d2) \\n\\n---\\n\\n### 4) **Name:** 48-human-hcca1-idab2-porta1-sec1-cel12-soma-z-corr-1-7 \\n**ID:** **b5914f7e-65a8-426f-aa09-4cea01ed6395** \\n**Brain region:** Field CA1, pyramidal layer (**CA1sp**, annotation_value: 407) \\n**Species:** *Homo sapiens* \\n**Mtype:** **Pyramidal Neuron** \\n**Created:** 2025-06-30T07:08:30.457779Z \\n**License:** **CC BY 4.0** \\n**Assets (examples):** SWC, ASC, H5 (asset IDs: 8e0212b2-5ac8-4d99-b96e-6520870ba737, a8e98103-abd5-4121-ba5d-9d3e76eae2d9, 61bf76ea-7922-4df3-b2bf-e1af60031c11) \\n**Link:** [View morphology](https://openbraininstitute.org/app/entity/b5914f7e-65a8-426f-aa09-4cea01ed6395) \\n\\n---\\n\\n### 5) **Name:** 46-human-hcca1-idab2-porta1-sec1-cel7-soma-z-corr-1-7 \\n**ID:** **33696a03-9e04-43bd-b893-fa65387e2e5f** \\n**Brain region:** Field CA1, pyramidal layer (**CA1sp**, annotation_value: 407) \\n**Species:** *Homo sapiens* \\n**Mtype:** **Pyramidal Neuron** \\n**Created:** 2025-06-30T07:08:24.806284Z \\n**License:** **CC BY 4.0** \\n**Assets (examples):** SWC, ASC, H5 (asset IDs: c067c026-d1e0-4094-80a6-46756107bd8a, 94723b49-57e2-427e-b359-6bc339f1ced5, 54ed991f-fdde-45e5-85e2-b1fd33b29748) \\n**Link:** [View morphology](https://openbraininstitute.org/app/entity/33696a03-9e04-43bd-b893-fa65387e2e5f) \\n\\n---\\n\\nIf you want: \\n- direct download links for the SWC/H5 files for any of these (I can fetch specific asset IDs), or \\n- a different region (e.g., DG, CA3) or species filter, say which one and I will retrieve them.", + "ai_response": "## Search result \u2014 hippocampal morphologies\n\n**Resolved brain region**\n- **Name:** Hippocampal region \n- **ID:** **08f3ff18-88db-48cc-bac5-d58c2fd16ec8** \n- **Annotation value:** **1080**\n\n**Cell morphology query within this region**\n- Query: morphologies within brain region **ID: 08f3ff18-88db-48cc-bac5-d58c2fd16ec8** \n- Returned **total_items: 0** (page 1, page_size 5)\n\nNo morphologies were found directly annotated to the top-level \"Hippocampal region\" entry in the selected hierarchy.\n\nNext steps (pick one)\n- Expand the search to include hippocampal subregions/descendants (e.g., CA1, CA3, dentate gyrus) and return 5 morphologies \u2014 this will search within descendant regions of the hippocampal hierarchy.\n- Perform a text-based search for morphologies with \"hippocampus\" in their metadata (name/description) and return 5 results.\n- Provide a list of hippocampal subregion IDs (CA1, CA3, DG, subiculum, etc.) so you can choose one to target.\n\nTell me which option you prefer (or say \"do option 1\" to expand to subregions).", "actual_tool_calls": [ { "name": "entitycore-brainregion-getall", @@ -2049,8 +2024,9 @@ { "name": "entitycore-cellmorphology-getall", "arguments": { - "within_brain_region_brain_region_id": "08f3ff18-88db-48cc-bac5-d58c2fd16ec8", - "within_brain_region_direction": "descendants" + "brain_region__annotation_value": 1080, + "brain_region__hierarchy_id": "e3e70682-c209-4cac-a29f-6fbed82c07cd", + "within_brain_region_brain_region_id": "08f3ff18-88db-48cc-bac5-d58c2fd16ec8" } } ], @@ -2058,10 +2034,10 @@ "metrics": [ { "name": "Correctness [GEval]", - "score": 0.4451658056800826, + "score": 0.28530009631183456, "success": false, "threshold": 0.5, - "reason": "The actual output provides detailed information about five morphologies from the hippocampus, which aligns with the request. However, it fails to match the expected output format, which includes placeholders for descriptions, morphological types, contributors, and licenses. Additionally, the species mentioned is *Homo sapiens* instead of the requested rodents (*Mus musculus*), indicating a lack of relevance to the specific input request." + "reason": "The actual output is relevant to the input as it pertains to hippocampal morphologies, but it fails to provide any actual morphologies as requested. Instead, it indicates that no morphologies were found, which does not align with the expected output that lists five specific morphologies. The structure is somewhat similar, but the lack of non-placeholder content significantly detracts from its effectiveness." }, { "name": "Tool Correctness", @@ -2075,24 +2051,24 @@ "score": 0.0, "success": false, "threshold": 0.5, - "reason": "The score is 0.00 because there were critical issues with the tool calls, including a lack of input parameters for fetching morphologies and no specification on limiting the results to 5. These oversights prevented the argument from being valid." + "reason": "The score is 0.00 because the input does not specify the number of morphologies to retrieve and fails to connect the specific IDs to the hippocampus as requested." }, { "name": "Deterministic Argument Correctness", - "score": 0.75, + "score": 0.6666666666666666, "success": true, "threshold": 0.5, "reason": "Incomplete tool usage: missing tools [ToolCall(\n name=\"entitycore-cellmorphology-getall\",\n input_parameters={\n \"within_brain_region_brain_region_id\": \"08f3ff18-88db-48cc-bac5-d58c2fd16ec8\"\n }\n)]; expected ['entitycore-brainregion-getall', 'entitycore-cellmorphology-getall'], called ['entitycore-brainregion-getall', 'entitycore-cellmorphology-getall']. See more details above." }, { "name": "Overall Argument Correctness", - "score": 0.75, + "score": 0.6666666666666666, "success": true, "threshold": 0.5, "reason": "" } ], - "created_at": "2025-11-28 16:33:30.264391" + "created_at": "2025-12-09 11:28:17.258667" } } -} \ No newline at end of file +} diff --git a/backend/eval/output/scores.json b/backend/eval/output/scores.json index 0d8a0e8c5..7336d5667 100644 --- a/backend/eval/output/scores.json +++ b/backend/eval/output/scores.json @@ -3,7 +3,7 @@ "metrics_df": [ { "test_name": "cerebellum_morphologies", - "Correctness [GEval]": 0.7353869349174808, + "Correctness [GEval]": 0.5378027691066317, "Tool Correctness": 1.0, "Argument Correctness": 0.0, "Deterministic Argument Correctness": 1.0, @@ -11,7 +11,7 @@ }, { "test_name": "cerebellum_morphologies_descendants", - "Correctness [GEval]": 0.7043457159658145, + "Correctness [GEval]": 0.7693448910375766, "Tool Correctness": 1.0, "Argument Correctness": 0.5, "Deterministic Argument Correctness": 1.0, @@ -19,7 +19,7 @@ }, { "test_name": "connectivity_metrics", - "Correctness [GEval]": 0.8413822094291581, + "Correctness [GEval]": 0.8282548737644898, "Tool Correctness": 1.0, "Argument Correctness": 0.5, "Deterministic Argument Correctness": 1.0, @@ -27,7 +27,7 @@ }, { "test_name": "connectivity_metrics_extra_filters", - "Correctness [GEval]": 0.8070269281059362, + "Correctness [GEval]": 0.8709177678372655, "Tool Correctness": 1.0, "Argument Correctness": 0.5, "Deterministic Argument Correctness": 1.0, @@ -35,31 +35,31 @@ }, { "test_name": "cortex_morphologies", - "Correctness [GEval]": 0.4653298006903742, - "Tool Correctness": 0.5, + "Correctness [GEval]": 0.5362164060624173, + "Tool Correctness": 1.0, "Argument Correctness": 0.0, - "Deterministic Argument Correctness": 0.0, - "Overall Argument Correctness": 0.0 + "Deterministic Argument Correctness": 1.0, + "Overall Argument Correctness": 1.0 }, { "test_name": "get_specific_circuit", - "Correctness [GEval]": 0.4548528662574201, - "Tool Correctness": 0.0, + "Correctness [GEval]": 0.8622402310750299, + "Tool Correctness": 1.0, "Argument Correctness": 1.0, - "Deterministic Argument Correctness": 0.0, + "Deterministic Argument Correctness": 1.0, "Overall Argument Correctness": 1.0 }, { "test_name": "hippocampus_morphologies", - "Correctness [GEval]": 0.4451658056800826, + "Correctness [GEval]": 0.28530009631183456, "Tool Correctness": 1.0, "Argument Correctness": 0.0, - "Deterministic Argument Correctness": 0.75, - "Overall Argument Correctness": 0.75 + "Deterministic Argument Correctness": 0.6666666666666666, + "Overall Argument Correctness": 0.6666666666666666 }, { "test_name": "ion_channel", - "Correctness [GEval]": 0.7807403647069058, + "Correctness [GEval]": 0.7820330992678872, "Tool Correctness": 1.0, "Argument Correctness": 1.0, "Deterministic Argument Correctness": 0.5, @@ -67,15 +67,15 @@ }, { "test_name": "ion_channel_recording", - "Correctness [GEval]": 0.8749718614233014, + "Correctness [GEval]": 0.5556731982805367, "Tool Correctness": 1.0, "Argument Correctness": 1.0, - "Deterministic Argument Correctness": 0.5, + "Deterministic Argument Correctness": 0.625, "Overall Argument Correctness": 1.0 }, { "test_name": "literature_search", - "Correctness [GEval]": 0.648942087564872, + "Correctness [GEval]": 0.9471472414785973, "Tool Correctness": 1.0, "Argument Correctness": 1.0, "Deterministic Argument Correctness": 0.0, @@ -83,15 +83,15 @@ }, { "test_name": "me_model_glossary", - "Correctness [GEval]": 0.683306831536382, - "Tool Correctness": 1.0, + "Correctness [GEval]": 0.5537974270369953, + "Tool Correctness": 0.0, "Argument Correctness": 1.0, - "Deterministic Argument Correctness": 1.0, + "Deterministic Argument Correctness": 0.0, "Overall Argument Correctness": 1.0 }, { "test_name": "morphology_studies", - "Correctness [GEval]": 0.4840374574429811, + "Correctness [GEval]": 0.8922801826803916, "Tool Correctness": 1.0, "Argument Correctness": 1.0, "Deterministic Argument Correctness": 0.0, @@ -99,7 +99,7 @@ }, { "test_name": "neuroscientists_search", - "Correctness [GEval]": 0.7001340077257124, + "Correctness [GEval]": 0.7275925796421102, "Tool Correctness": 1.0, "Argument Correctness": 1.0, "Deterministic Argument Correctness": 0.0, @@ -107,7 +107,7 @@ }, { "test_name": "off_topic_cooking", - "Correctness [GEval]": 0.5945247609273225, + "Correctness [GEval]": 0.5978033804771575, "Tool Correctness": 1.0, "Argument Correctness": 1.0, "Deterministic Argument Correctness": 1.0, @@ -115,7 +115,7 @@ }, { "test_name": "off_topic_programming", - "Correctness [GEval]": 0.7695570160957931, + "Correctness [GEval]": 0.8565314307651558, "Tool Correctness": 1.0, "Argument Correctness": 1.0, "Deterministic Argument Correctness": 1.0, @@ -123,7 +123,7 @@ }, { "test_name": "off_topic_sports", - "Correctness [GEval]": 0.8057059705376057, + "Correctness [GEval]": 0.7509455475364661, "Tool Correctness": 1.0, "Argument Correctness": 1.0, "Deterministic Argument Correctness": 1.0, @@ -131,7 +131,7 @@ }, { "test_name": "off_topic_weather", - "Correctness [GEval]": 0.6890006117592387, + "Correctness [GEval]": 0.5413464829505792, "Tool Correctness": 1.0, "Argument Correctness": 1.0, "Deterministic Argument Correctness": 1.0, @@ -139,7 +139,7 @@ }, { "test_name": "platform_explore", - "Correctness [GEval]": 0.2853000957950641, + "Correctness [GEval]": 0.27294881512648356, "Tool Correctness": 1.0, "Argument Correctness": 1.0, "Deterministic Argument Correctness": 1.0, @@ -147,7 +147,7 @@ }, { "test_name": "platform_news", - "Correctness [GEval]": 0.8330807047145509, + "Correctness [GEval]": 0.804995298303768, "Tool Correctness": 1.0, "Argument Correctness": 0.0, "Deterministic Argument Correctness": 1.0, @@ -155,7 +155,7 @@ }, { "test_name": "platform_ui_simulate", - "Correctness [GEval]": 0.5975963651681446, + "Correctness [GEval]": 0.34272411774196826, "Tool Correctness": 1.0, "Argument Correctness": 1.0, "Deterministic Argument Correctness": 1.0, @@ -163,7 +163,7 @@ }, { "test_name": "platform_viewing", - "Correctness [GEval]": 0.6731008652407333, + "Correctness [GEval]": 0.72747771401505, "Tool Correctness": 1.0, "Argument Correctness": 0.0, "Deterministic Argument Correctness": 1.0, @@ -171,7 +171,7 @@ }, { "test_name": "plotting", - "Correctness [GEval]": 0.7366374977736997, + "Correctness [GEval]": 0.6127246801610241, "Tool Correctness": 1.0, "Argument Correctness": 1.0, "Deterministic Argument Correctness": 0.5, @@ -179,15 +179,15 @@ }, { "test_name": "read_paper", - "Correctness [GEval]": 0.32354476156942, - "Tool Correctness": 1.0, - "Argument Correctness": 1.0, - "Deterministic Argument Correctness": 1.0, - "Overall Argument Correctness": 1.0 + "Correctness [GEval]": 0.7122372263400929, + "Tool Correctness": 0.0, + "Argument Correctness": 0.0, + "Deterministic Argument Correctness": 0.0, + "Overall Argument Correctness": 0.0 }, { "test_name": "sin_plot", - "Correctness [GEval]": 0.43660922801944063, + "Correctness [GEval]": 0.7415472928297893, "Tool Correctness": 1.0, "Argument Correctness": 1.0, "Deterministic Argument Correctness": 0.0, @@ -195,23 +195,23 @@ }, { "test_name": "software_docs_entitysdk", - "Correctness [GEval]": 0.7795970779907103, + "Correctness [GEval]": 0.6813881453094685, "Tool Correctness": 1.0, "Argument Correctness": 1.0, - "Deterministic Argument Correctness": 0.0, + "Deterministic Argument Correctness": 0.2, "Overall Argument Correctness": 1.0 }, { "test_name": "software_docs_obione", - "Correctness [GEval]": 0.6725945299889496, + "Correctness [GEval]": 0.8310577292713972, "Tool Correctness": 1.0, "Argument Correctness": 1.0, - "Deterministic Argument Correctness": 0.0, + "Deterministic Argument Correctness": 0.2, "Overall Argument Correctness": 1.0 }, { "test_name": "species_list", - "Correctness [GEval]": 0.6400358146726484, + "Correctness [GEval]": 0.6566399827580228, "Tool Correctness": 1.0, "Argument Correctness": 1.0, "Deterministic Argument Correctness": 1.0, @@ -219,7 +219,7 @@ }, { "test_name": "thalamus_id", - "Correctness [GEval]": 0.7415867699349027, + "Correctness [GEval]": 0.659614380970777, "Tool Correctness": 1.0, "Argument Correctness": 1.0, "Deterministic Argument Correctness": 0.5, @@ -227,7 +227,7 @@ }, { "test_name": "warning_test", - "Correctness [GEval]": 0.618138951957894, + "Correctness [GEval]": 0.7849753604657479, "Tool Correctness": 1.0, "Argument Correctness": 1.0, "Deterministic Argument Correctness": 0.0, @@ -235,12 +235,12 @@ }, { "test_name": "web_search", - "Correctness [GEval]": 0.4164325021025331, + "Correctness [GEval]": 0.7085964582401003, "Tool Correctness": 1.0, "Argument Correctness": 1.0, "Deterministic Argument Correctness": 0.0, "Overall Argument Correctness": 1.0 } ], - "created_at": "2025-11-28 16:33:30.267246" -} \ No newline at end of file + "created_at": "2025-12-09 11:28:17.260619" +} diff --git a/backend/src/neuroagent/agent_routine.py b/backend/src/neuroagent/agent_routine.py index 9faa8a1e5..e39d455ac 100644 --- a/backend/src/neuroagent/agent_routine.py +++ b/backend/src/neuroagent/agent_routine.py @@ -9,16 +9,30 @@ from typing import Any, AsyncIterator from openai import AsyncOpenAI, AsyncStream -from openai.types.chat.chat_completion_chunk import ChatCompletionChunk +from openai.types.responses import ( + ResponseCompletedEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionToolCall, + ResponseFunctionToolCallOutputItem, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseOutputMessage, + ResponseReasoningItem, + ResponseReasoningSummaryPartAddedEvent, + ResponseReasoningSummaryPartDoneEvent, + ResponseReasoningSummaryTextDeltaEvent, + ResponseStreamEvent, + ResponseTextDeltaEvent, +) from pydantic import BaseModel, ValidationError from neuroagent.app.database.sql_schemas import ( Entity, Messages, + PartType, Task, - TokenConsumption, - TokenType, - ToolCalls, ) from neuroagent.new_types import ( Agent, @@ -27,10 +41,11 @@ ) from neuroagent.tools.base_tool import BaseTool from neuroagent.utils import ( + append_part, complete_partial_json, - get_entity, - get_token_count, - merge_chunk, + get_main_LLM_token_consumption, + get_previous_hil_metadata, + get_tool_token_consumption, messages_to_openai_content, ) @@ -52,7 +67,7 @@ async def get_chat_completion( context_variables: dict[str, Any], model_override: str | None, stream: bool = False, - ) -> AsyncStream[ChatCompletionChunk]: + ) -> AsyncStream[ResponseStreamEvent]: """Send the OpenAI request.""" context_variables = defaultdict(str, context_variables) instructions = ( @@ -60,27 +75,31 @@ async def get_chat_completion( if callable(agent.instructions) else agent.instructions ) - messages = [{"role": "system", "content": instructions}] + history tools = [tool.pydantic_to_openai_schema() for tool in agent.tools] create_params = { - "messages": messages, + "instructions": instructions, + "input": history, "model": model_override or agent.model, "stream": stream, - "seed": 12008, "temperature": agent.temperature, - "tools": tools or None, - "tool_choice": agent.tool_choice, + "tools": tools or [], + "include": ["reasoning.encrypted_content"], + "store": False, + "text": {"verbosity": "medium"}, } - if stream: - create_params["stream_options"] = {"include_usage": True} + + if agent.tool_choice: + create_params["tool_choice"] = agent.tool_choice + if agent.reasoning is not None: - create_params["reasoning_effort"] = agent.reasoning + create_params["reasoning"] = {"effort": agent.reasoning, "summary": "auto"} if tools: create_params["parallel_tool_calls"] = agent.parallel_tool_calls - return await self.client.chat.completions.create(**create_params) # type: ignore + + return await self.client.responses.create(**create_params) # type: ignore def handle_function_result(self, result: Result | Agent | BaseModel) -> Result: """Check if agent handoff or regular tool call.""" @@ -104,7 +123,7 @@ def handle_function_result(self, result: Result | Agent | BaseModel) -> Result: async def execute_tool_calls( self, - tool_calls: list[ToolCalls], + tool_calls: list[ResponseFunctionToolCall], tools: list[type[BaseTool]], context_variables: dict[str, Any], ) -> Response: @@ -132,23 +151,24 @@ async def execute_tool_calls( async def handle_tool_call( self, - tool_call: ToolCalls, + tool_call: ResponseFunctionToolCall, tools: list[type[BaseTool]], context_variables: dict[str, Any], raise_validation_errors: bool = False, - ) -> tuple[dict[str, str], Agent | None]: + ) -> tuple[ResponseFunctionToolCallOutputItem, Agent | None]: """Run individual tools.""" tool_map = {tool.name: tool for tool in tools} name = tool_call.name # handle missing tool case, skip to next tool if name not in tool_map: - return { - "role": "tool", - "tool_call_id": tool_call.tool_call_id, - "tool_name": name, - "content": f"Error: Tool {name} not found.", - }, None + return ResponseFunctionToolCallOutputItem( + id=tool_call.id or "", + call_id=tool_call.call_id, + status="incomplete", + output=f"Error: Tool {name} not found.", + type="function_call_output", + ), None kwargs = json.loads(tool_call.arguments) tool = tool_map[name] @@ -160,13 +180,13 @@ async def handle_tool_call( raise err else: # Otherwise transform it into an OpenAI response for the model to retry - response = { - "role": "tool", - "tool_call_id": tool_call.tool_call_id, - "tool_name": name, - "content": err.json(), - } - return response, None + return ResponseFunctionToolCallOutputItem( + id=tool_call.id or "", + call_id=tool_call.call_id, + status="incomplete", + output=err.json(), + type="function_call_output", + ), None try: tool_metadata = tool.__annotations__["metadata"](**context_variables) @@ -176,13 +196,13 @@ async def handle_tool_call( raise err else: # Otherwise transform it into an OpenAI response for the model to retry - response = { - "role": "tool", - "tool_call_id": tool_call.tool_call_id, - "tool_name": name, - "content": "The user is not allowed to run this tool. Don't call it again.", - } - return response, None + return ResponseFunctionToolCallOutputItem( + id=tool_call.id or "", + call_id=tool_call.call_id, + status="incomplete", + output="The user is not allowed to run this tool. Don't call it again.", + type="function_call_output", + ), None logger.info( f"Entering {name}. Inputs: {input_schema.model_dump(exclude_defaults=True)}." @@ -192,30 +212,26 @@ async def handle_tool_call( try: raw_result = await tool_instance.arun() if hasattr(tool_instance.metadata, "token_consumption"): - context_variables["usage_dict"][tool_call.tool_call_id] = ( + context_variables["usage_dict"][tool_call.call_id] = ( tool_instance.metadata.token_consumption ) except Exception as err: - response = { - "role": "tool", - "tool_call_id": tool_call.tool_call_id, - "tool_name": name, - "content": str(err), - } - return response, None + return ResponseFunctionToolCallOutputItem( + id=tool_call.id or "", + call_id=tool_call.call_id, + status="incomplete", + output=str(err), + type="function_call_output", + ), None result: Result = self.handle_function_result(raw_result) - response = { - "role": "tool", - "tool_call_id": tool_call.tool_call_id, - "tool_name": name, - "content": result.value, - } - if result.agent: - agent = result.agent - else: - agent = None - return response, agent + return ResponseFunctionToolCallOutputItem( + id=tool_call.id or "", + call_id=tool_call.call_id, + status="completed", + output=result.value, + type="function_call_output", + ), result.agent async def astream( self, @@ -231,10 +247,30 @@ async def astream( active_agent = agent content = await messages_to_openai_content(messages) history = copy.deepcopy(content) - tool_map = {tool.name: tool for tool in agent.tools} + turns = 0 + metadata_data: list[dict[str, Any]] = [] + + # If new message, create it. Else, HIL to we take the previous Assistant message. + if messages[-1].entity == Entity.USER: + new_message = Messages( + thread_id=messages[-1].thread_id, + entity=Entity.ASSISTANT, + parts=[], + ) + yield f"data: {json.dumps({'type': 'start', 'messageId': f'msg_{uuid.uuid4().hex}'})}\n\n" + else: + new_message = messages[-1] + await new_message.awaitable_attrs.token_consumption + # Initialize metadata for previous HIL tool calls + tool_map = {tool.name: tool for tool in active_agent.tools} + metadata_data = get_previous_hil_metadata(new_message, tool_map) + # MAIN AGENT LOOP while turns <= max_turns: + # We need to redefine the tool map since the tools can change on agent switch. + tool_map = {tool.name: tool for tool in active_agent.tools} + # Force an AI message once max turns reached. # I.e. we do a total number of turns of max_turns + 1 # The +1 being the final AI message. @@ -242,21 +278,6 @@ async def astream( agent.tool_choice = "none" agent.instructions = "You are a very nice assistant that is unable to further help the user due to rate limiting. The user just reached the maximum amount of turns he can take with you in a single query. Your one and only job is to let him know that in a nice way, and that the only way to continue the conversation is to send another message. Completely disregard his demand since you cannot fulfill it, simply state that he reached the limit." - message: dict[str, Any] = { - "content": "", - "reasoning": "", - "sender": agent.name, - "role": "assistant", - "function_call": None, - "tool_calls": defaultdict( - lambda: { - "function": {"arguments": "", "name": ""}, - "id": "", - "type": "", - } - ), - } - # get completion with current history, agent completion = await self.get_chat_completion( agent=active_agent, @@ -267,346 +288,317 @@ async def astream( ) turns += 1 - draft_tool_calls: list[dict[str, str]] = [] - draft_tool_calls_index = -1 - async for chunk in completion: - for choice in chunk.choices: - if choice.finish_reason == "stop": - if choice.delta.content: - yield f"0:{json.dumps(choice.delta.content, separators=(',', ':'))}\n" - - elif choice.finish_reason == "tool_calls": - # Some models stream the whole tool call in one chunk. - if not draft_tool_calls and choice.delta.tool_calls: - for tc in choice.delta.tool_calls: - tc.id = uuid.uuid4().hex - draft_tool_calls.append( - { - "arguments": tc.function.arguments or "{}" - if tc.function - else "{}", - "id": tc.id, - "name": tc.function.name or "" - if tc.function - else "", - } - ) - - for draft_tool_call in draft_tool_calls: - input_args = json.loads( - draft_tool_call["arguments"] or "{}" - ) - try: - input_schema: type[BaseModel] = tool_map[ - draft_tool_call["name"] - ].__annotations__["input_schema"] - - args = input_schema(**input_args).model_dump( - mode="json" - ) - except ValidationError: - args = input_args - tool_call_data = { - "toolCallId": draft_tool_call["id"], - "toolName": draft_tool_call["name"], - "args": args, - } - yield f"9:{json.dumps(tool_call_data, separators=(',', ':'))}\n" - - # Check for tool calls - elif choice.delta.tool_calls: - for tool_call in choice.delta.tool_calls: - if tool_call is None: - continue - if tool_call.function is None: - continue - if tool_call.id is not None: - tool_call.id = ( - uuid.uuid4().hex - ) # Set provider_id to random uuid - - id = tool_call.id - name = tool_call.function.name - arguments = tool_call.function.arguments - if id is not None: - draft_tool_calls_index += 1 - draft_tool_calls.append( - {"id": id, "name": name, "arguments": ""} # type: ignore - ) - tool_begin_data = { - "toolCallId": id, - "toolName": name, - } - yield f"b:{json.dumps(tool_begin_data, separators=(',', ':'))}\n" - - if arguments: - current_id = ( - id - or draft_tool_calls[draft_tool_calls_index][ - "id" - ] - ) - args_data = { - "toolCallId": current_id, - "argsTextDelta": arguments, - } - yield f"c:{json.dumps(args_data, separators=(',', ':'))}\n" - draft_tool_calls[draft_tool_calls_index][ - "arguments" - ] += arguments - elif ( - hasattr(choice.delta, "reasoning") - and choice.delta.reasoning + usage_data = None + # for streaming interrupt and handling tool calls + temp_stream_data: dict[str, Any] = { + "content": dict[str, ResponseOutputMessage](), + "tool_calls": dict[str, ResponseFunctionToolCall](), + "reasoning": dict[str, ResponseReasoningItem](), + "tool_to_execute": dict[str, ResponseFunctionToolCall](), + } + # Unpack the streaming events + async for event in completion: + match event: + # === REASONING === + # Reasoning starts + case ResponseOutputItemAddedEvent() if ( + isinstance(event.item, ResponseReasoningItem) + and event.item.id ): - yield f"g:{json.dumps(choice.delta.reasoning, separators=(',', ':'))}\n\n" - - else: - if choice.delta.content is not None: - yield f"0:{json.dumps(choice.delta.content, separators=(',', ':'))}\n" - - delta_json = choice.delta.model_dump() - delta_json.pop("role", None) - merge_chunk(message, delta_json) - - if chunk.choices == []: - finish_data = { - "finishReason": "tool-calls" - if len(draft_tool_calls) > 0 - else "stop", - } - else: - finish_data = {"finishReason": "stop"} - - message["tool_calls"] = list(message.get("tool_calls", {}).values()) - if not message["tool_calls"]: - message["tool_calls"] = None - - # If tool calls requested, instantiate them as an SQL compatible class + temp_stream_data["reasoning"][event.item.id] = ( + ResponseReasoningItem( + id=event.item.id, + summary=[], + type="reasoning", + ) + ) - if message["tool_calls"]: - tool_calls = [ - ToolCalls( - tool_call_id=tool_call["id"], - name=tool_call["function"]["name"], - arguments=tool_call["function"]["arguments"], - ) - for tool_call in message["tool_calls"] - ] - else: - tool_calls = [] + # Reasoning summary start + case ResponseReasoningSummaryPartAddedEvent(): + temp_stream_data["reasoning"][event.item_id].summary.append( + {"text": "", "type": "summary_text"} + ) + yield f"data: {json.dumps({'type': 'start-step'})}\n\n" + yield f"data: {json.dumps({'type': 'reasoning-start', 'id': event.item_id})}\n\n" + + # Reasoning summary deltas + case ResponseReasoningSummaryTextDeltaEvent(): + temp_stream_data["reasoning"][event.item_id].summary[-1][ + "text" + ] += event.delta + yield f"data: {json.dumps({'type': 'reasoning-delta', 'id': event.item_id, 'delta': event.delta})}\n\n" + + # Reasoning summary end + case ResponseReasoningSummaryPartDoneEvent(): + yield f"data: {json.dumps({'type': 'reasoning-end', 'id': event.item_id})}\n\n" + yield f"data: {json.dumps({'type': 'finish-step'})}\n\n" + + # Capture the final reasoning (only chunk that has the encrypted content) + case ResponseOutputItemDoneEvent() if ( + isinstance(event.item, ResponseReasoningItem) + and event.item.id + ): + append_part( + new_message, history, event.item, PartType.REASONING + ) + temp_stream_data["reasoning"].pop(event.item.id, None) - # Append the history with the json version - history.append(copy.deepcopy(message)) + # === TEXT === + # Text message starts + case ResponseOutputItemAddedEvent() if ( + isinstance(event.item, ResponseOutputMessage) + and event.item.id + ): + temp_stream_data["content"][event.item.id] = ( + ResponseOutputMessage( + id=event.item.id, + content=[], + role="assistant", + status="in_progress", + type="message", + ) + ) - # We add a true / false to check if there were tool calls. - message["tool_calls"] = ( - "tool_calls" in message and message["tool_calls"] - ) + # Text start + case ResponseContentPartAddedEvent(): + temp_stream_data["content"][event.item_id].content.append( + {"text": "", "type": "output_text"} + ) + yield f"data: {json.dumps({'type': 'text-start', 'id': event.item_id})}\n\n" + + # Text Delta + case ResponseTextDeltaEvent(): + temp_stream_data["content"][event.item_id].content[-1][ + "text" + ] += event.delta + yield f"data: {json.dumps({'type': 'text-delta', 'id': event.item_id, 'delta': event.delta})}\n\n" + + # Text end + case ResponseContentPartDoneEvent(): + yield f"data: {json.dumps({'type': 'text-end', 'id': event.item_id})}\n\n" + yield f"data: {json.dumps({'type': 'finish-step'})}\n\n" + + # Capture the final content part + case ResponseOutputItemDoneEvent() if ( + isinstance(event.item, ResponseOutputMessage) + and event.item.id + ): + append_part( + new_message, history, event.item, PartType.MESSAGE + ) + temp_stream_data["content"].pop(event.item.id, None) - # Stage the new message for addition to DB - token_count = get_token_count(chunk.usage) - token_consumption = [ - TokenConsumption( - type=token_type, - task=Task.CHAT_COMPLETION, - count=count, - model=agent.model, - ) - for token_type, count in [ - (TokenType.INPUT_CACHED, token_count["input_cached"]), - (TokenType.INPUT_NONCACHED, token_count["input_noncached"]), - (TokenType.COMPLETION, token_count["completion"]), - ] - if count - ] - messages.append( - Messages( - thread_id=messages[-1].thread_id, - entity=get_entity(message), - content=json.dumps(message), - tool_calls=tool_calls, - is_complete=True, - token_consumption=token_consumption, + # === TOOL CALLS === + # Tool call starts + case ResponseOutputItemAddedEvent() if ( + isinstance(event.item, ResponseFunctionToolCall) + and event.item.id + ): + temp_stream_data["tool_calls"][event.item.id] = ( + ResponseFunctionToolCall( + id=event.item.id, + call_id=event.item.call_id, + name=event.item.name, + arguments="", + type="function_call", + status="in_progress", + ) + ) + yield f"data: {json.dumps({'type': 'start-step'})}\n\n" + yield f"data: {json.dumps({'type': 'tool-input-start', 'toolCallId': event.item.call_id, 'toolName': event.item.name})}\n\n" + + # Tool call (args) deltas + case ResponseFunctionCallArgumentsDeltaEvent() if event.item_id: + # Now call ID in the stream, we have to get it form temp stream data + tool_call_id = temp_stream_data["tool_calls"][ + event.item_id + ].call_id + temp_stream_data["tool_calls"][ + event.item_id + ].arguments += event.delta + yield f"data: {json.dumps({'type': 'tool-input-delta', 'toolCallId': tool_call_id, 'inputTextDelta': event.delta})}\n\n" + + # Tool call end and ready to execute + case ResponseOutputItemDoneEvent() if ( + isinstance(event.item, ResponseFunctionToolCall) + and event.item.id + ): + input_args = event.item.arguments + try: + input_schema: type[BaseModel] = tool_map[ + event.item.name + ].__annotations__["input_schema"] + validated_args = input_schema( + **json.loads(input_args) + ).model_dump(mode="json") + args = json.dumps(validated_args) + except ValidationError: + args = input_args + append_part( + new_message, history, event.item, PartType.FUNCTION_CALL + ) + # Tool call ready --> remove from tool_calls, add to tool_to_execute + temp_stream_data["tool_calls"].pop(event.item.id, None) + temp_stream_data["tool_to_execute"][event.item.id] = ( + event.item + ) + yield f"data: {json.dumps({'type': 'tool-input-available', 'toolCallId': event.item.call_id, 'toolName': event.item.name, 'input': json.loads(args)})}\n\n" + yield f"data: {json.dumps({'type': 'finish-step'})}\n\n" + + # === Usage === + # Handle usage/token information and ecrypted reasoning. + case ResponseCompletedEvent(): + usage_data = event.response.usage + + # case _: + # print(event.type) + # Some events are not needed. Not sure what we should do with them yet. + + # Add the main LLM token usage + new_message.token_consumption.extend( + get_main_LLM_token_consumption( + usage_data, agent.model, Task.CHAT_COMPLETION ) ) - if not messages[-1].tool_calls: - yield f"e:{json.dumps(finish_data)}\n" + # Separate streamed tool --> tool to execute / tool with HIL + if temp_stream_data["tool_to_execute"]: + tool_calls_to_execute = [ + tc + for tc in temp_stream_data["tool_to_execute"].values() + if not tool_map[tc.name].hil + ] + tool_calls_with_hil = [ + tc + for tc in temp_stream_data["tool_to_execute"].values() + if tool_map[tc.name].hil + ] + else: + # No tool calls, final content part reached, exit agent loop + yield f"data: {json.dumps({'type': 'finish-step'})}\n\n" break - # kick out tool calls that require HIL - tool_calls_to_execute = [ - tool_call - for tool_call in messages[-1].tool_calls - if not tool_map[tool_call.name].hil - ] - - tool_calls_with_hil = [ - tool_call - for tool_call in messages[-1].tool_calls - if tool_map[tool_call.name].hil - ] - # handle function calls, updating context_variables, and switching agents if tool_calls_to_execute: - tool_calls_executed = await self.execute_tool_calls( + tool_calls_done = await self.execute_tool_calls( tool_calls_to_execute[:max_parallel_tool_calls], active_agent.tools, context_variables, ) - tool_calls_executed.messages.extend( + tool_calls_done.messages.extend( [ - { - "role": "tool", - "tool_call_id": call.tool_call_id, - "tool_name": call.name, - "content": f"The tool {call.name} with arguments {call.arguments} could not be executed due to rate limit. Call it again.", - } + ResponseFunctionToolCallOutputItem( + id=call.id or "", + call_id=call.call_id, + output=f"The tool {call.name} with arguments {call.arguments} could not be executed due to rate limit. Call it again.", + type="function_call_output", + status="incomplete", + ) for call in tool_calls_to_execute[max_parallel_tool_calls:] ] ) else: - tool_calls_executed = Response( + tool_calls_done = Response( messages=[], agent=None, context_variables=context_variables ) - # Before extending history, yield each tool response - for tool_response in tool_calls_executed.messages: - response_data = { - "toolCallId": tool_response["tool_call_id"], - "result": tool_response["content"], - } - yield f"a:{json.dumps(response_data, separators=(',', ':'))}\n" - - yield f"e:{json.dumps(finish_data)}\n" - - for tool_response in tool_calls_executed.messages: - # Check if an LLM has been called inside of the tool - if context_variables["usage_dict"].get( - tool_response["tool_call_id"] - ): - # Get the consumption dict for the given tool - tool_call_consumption = context_variables["usage_dict"][ - tool_response["tool_call_id"] - ] - - # Set consumption in SQL classess - token_consumption = [ - TokenConsumption( - type=token_type, - task=Task.CALL_WITHIN_TOOL, - count=count, - model=tool_call_consumption["model"], - ) - for token_type, count in [ - ( - TokenType.INPUT_CACHED, - tool_call_consumption["input_cached"], - ), - ( - TokenType.INPUT_NONCACHED, - tool_call_consumption["input_noncached"], - ), - ( - TokenType.COMPLETION, - tool_call_consumption["completion"], - ), - ] - if count - ] - else: - token_consumption = [] - - messages.append( - Messages( - thread_id=messages[-1].thread_id, - entity=Entity.TOOL, - content=json.dumps(tool_response), - is_complete=True, - token_consumption=token_consumption, - ) + # Process tool call outputs, adding token consumption and yielding outputs + for tool_response in tool_calls_done.messages: + new_message.token_consumption.extend( + get_tool_token_consumption(tool_response, context_variables) + ) + append_part( + new_message, + history, + tool_response, + PartType.FUNCTION_CALL_OUTPUT, ) + temp_stream_data["tool_to_execute"].pop(tool_response.id, None) + yield f"data: {json.dumps({'type': 'tool-output-available', 'toolCallId': tool_response.call_id, 'output': tool_response.output})}\n\n" + + yield f"data: {json.dumps({'type': 'finish-step'})}\n\n" - # If the tool call response contains HIL validation, do not update anything and return + # If response contains HIL validation, do not update anything and return if tool_calls_with_hil: - annotation_data = [ - {"toolCallId": msg.tool_call_id, "validated": "pending"} - for msg in tool_calls_with_hil - ] + for msg in tool_calls_with_hil: + metadata_data.append( + { + "toolCallId": msg.call_id, + "validated": "pending", + "isComplete": True, + } + ) + temp_stream_data["tool_to_execute"].pop(msg.id, None) - yield f"8:{json.dumps(annotation_data, separators=(',', ':'))}\n" - yield f"e:{json.dumps(finish_data)}\n" + yield f"data: {json.dumps({'type': 'finish-step'})}\n\n" break - history.extend(tool_calls_executed.messages) - context_variables.update(tool_calls_executed.context_variables) - if tool_calls_executed.agent: - active_agent = tool_calls_executed.agent + # Update context variables, agent + context_variables.update(tool_calls_done.context_variables) + if tool_calls_done.agent: + active_agent = tool_calls_done.agent - done_data = { - "finishReason": "stop", - } - yield f"d:{json.dumps(done_data)}\n" + # End of agent loop. Add new message to DB. + messages.append(new_message) + if metadata_data: + yield f"data: {json.dumps({'type': 'finish', 'messageMetadata': {'toolCalls': metadata_data}})}\n\n" + else: + yield f"data: {json.dumps({'type': 'finish'})}\n\n" + yield "data: [DONE]\n\n" # User interrupts streaming except asyncio.exceptions.CancelledError: - if isinstance(message["tool_calls"], defaultdict): - message["tool_calls"] = list(message.get("tool_calls", {}).values()) + # add parts not appended to `new_message` + if temp_stream_data["reasoning"]: + for reasoning_item in temp_stream_data["reasoning"].values(): + del reasoning_item.id + append_part( + new_message, history, reasoning_item, PartType.REASONING, False + ) - if not message["tool_calls"]: - message["tool_calls"] = None - else: - # Attempt to fix partial JSONs if any - for elem in message["tool_calls"]: - elem["function"]["arguments"] = complete_partial_json( - elem["function"]["arguments"] + if temp_stream_data["content"]: + for message_item in temp_stream_data["content"].values(): + message_item.status = "incomplete" + append_part( + new_message, history, message_item, PartType.MESSAGE, False ) - logger.debug(f"Stream interrupted. Partial message {message}") - - if message["tool_calls"]: - tool_calls = [ - ToolCalls( - tool_call_id=tool_call["id"], - name=tool_call["function"]["name"], - arguments=tool_call["function"]["arguments"], + + if temp_stream_data["tool_calls"]: + for tool_call in temp_stream_data["tool_calls"].values(): + tool_call.arguments = complete_partial_json(tool_call.arguments) + tool_call.status = "incomplete" + append_part( + new_message, history, tool_call, PartType.FUNCTION_CALL, False ) - for tool_call in message["tool_calls"] - ] - else: - tool_calls = [] - - # If the partial message hasn't been appended and the last message is not an AI_TOOL, append partial message - if ( - json.dumps(message) != messages[-1].content - and messages[-1].entity != Entity.AI_TOOL - ): - messages.append( - Messages( - thread_id=messages[-1].thread_id, - entity=get_entity(message), - content=json.dumps(message), - tool_calls=tool_calls, - is_complete=False, + append_part( + new_message, + history, + ResponseFunctionToolCallOutputItem( + id=tool_call.id, + call_id=tool_call.call_id, + output="Tool execution aborted by the user.", + type="function_call_output", + status="incomplete", + ), + PartType.FUNCTION_CALL_OUTPUT, + False, ) - ) - # Append default tool message to partial tool calls - if messages[-1].entity == Entity.AI_TOOL: - messages.extend( - [ - Messages( - thread_id=messages[-1].thread_id, - entity=Entity.TOOL, - content=json.dumps( - { - "role": "tool", - "tool_call_id": call.tool_call_id, - "tool_name": call.name, - "content": "Tool execution aborted by the user.", - } - ), - is_complete=False, - ) - for call in tool_calls - ] - ) + if temp_stream_data["tool_to_execute"]: + for tool_call in temp_stream_data["tool_to_execute"].values(): + append_part( + new_message, + history, + ResponseFunctionToolCallOutputItem( + id=tool_call.id, + call_id=tool_call.call_id, + output="Tool execution aborted by the user.", + type="function_call_output", + status="incomplete", + ), + PartType.FUNCTION_CALL_OUTPUT, + False, + ) + + # Append completed new_message to DB + messages.append(new_message) diff --git a/backend/src/neuroagent/app/app_utils.py b/backend/src/neuroagent/app/app_utils.py index c936fd319..001bc2e27 100644 --- a/backend/src/neuroagent/app/app_utils.py +++ b/backend/src/neuroagent/app/app_utils.py @@ -19,6 +19,7 @@ ComplexityEstimation, Entity, Messages, + PartType, ReasoningLevels, Task, Threads, @@ -28,19 +29,21 @@ utc_now, ) from neuroagent.app.schemas import ( - AnnotationMessageVercel, - AnnotationToolCallVercel, MessagesRead, MessagesReadVercel, + MetadataToolCallVercel, PaginatedResponse, RateLimitInfo, ReasoningPartVercel, TextPartVercel, ToolCallPartVercel, - ToolCallVercel, + ToolMetadataDict, ) from neuroagent.tools.base_tool import BaseTool -from neuroagent.utils import get_token_count, messages_to_openai_content +from neuroagent.utils import ( + get_token_count, + messages_to_openai_content, +) logger = logging.getLogger(__name__) @@ -195,51 +198,33 @@ async def commit_messages( def format_messages_output( db_messages: Sequence[Messages], - tool_hil_mapping: dict[str, bool], has_more: bool, page_size: int, ) -> PaginatedResponse[MessagesRead]: """Format db messages to regular output schema.""" - messages = [] + messages: list[MessagesRead] = [] for msg in db_messages: - # Create a clean dict without SQLAlchemy attributes - message_data = { - "message_id": msg.message_id, - "entity": msg.entity.value, # Convert enum to string - "thread_id": msg.thread_id, - "is_complete": msg.is_complete, - "creation_date": msg.creation_date.isoformat(), # Convert datetime to string - "msg_content": json.loads(msg.content), - } + parts_data: list[dict[str, Any]] = [] + for part in msg.parts: + output = part.output or {} + content = output.get("content", []) - # Map validation status based on tool requirements - tool_calls_data = [] - for tc in msg.tool_calls: - requires_validation = tool_hil_mapping.get(tc.name, False) - - if tc.validated is True: - validation_status = "accepted" - elif tc.validated is False: - validation_status = "rejected" - elif not requires_validation: - validation_status = "not_required" - else: - validation_status = "pending" - - tool_calls_data.append( - { - "tool_call_id": tc.tool_call_id, - "name": tc.name, - "arguments": tc.arguments, - "validated": validation_status, - } - ) + for item in content: + if item.get("type") == "text": + parts_data.append({"type": "text", "text": item.get("text", "")}) - message_data["tool_calls"] = tool_calls_data - messages.append(MessagesRead(**message_data)) + messages.append( + MessagesRead( + message_id=msg.message_id, + entity=msg.entity.value, + thread_id=msg.thread_id, + creation_date=msg.creation_date, + parts=parts_data, + ) + ) return PaginatedResponse( - next_cursor=messages[-1].creation_date, + next_cursor=db_messages[-1].creation_date if has_more else None, has_more=has_more, page_size=page_size, results=messages, @@ -253,147 +238,84 @@ def format_messages_vercel( page_size: int, ) -> PaginatedResponse[MessagesReadVercel]: """Format db messages to Vercel schema.""" - messages: list[MessagesReadVercel] = [] - parts: list[TextPartVercel | ToolCallPartVercel | ReasoningPartVercel] = [] - annotations: list[AnnotationMessageVercel | AnnotationToolCallVercel] = [] - - for msg in reversed(db_messages): - if msg.entity in [Entity.USER, Entity.AI_MESSAGE]: - content = json.loads(msg.content) - text_content = content.get("content") - reasoning_content = content.get("reasoning") - - # Optional reasoning - if reasoning_content: - parts.append(ReasoningPartVercel(reasoning=reasoning_content)) - - message_data = { - "id": msg.message_id, - "role": "user" if msg.entity == Entity.USER else "assistant", - "createdAt": msg.creation_date, - "content": text_content, - } - # add tool calls and reset buffer after attaching - if msg.entity == Entity.AI_MESSAGE: - if text_content: - parts.append(TextPartVercel(text=text_content)) - - annotations.append( - AnnotationMessageVercel( - messageId=msg.message_id, isComplete=msg.is_complete - ) + messages = [] + for msg in db_messages: + parts_data: list[ToolCallPartVercel | TextPartVercel | ReasoningPartVercel] = [] + tool_calls: dict[str, ToolCallPartVercel] = {} + metadata: dict[str, MetadataToolCallVercel] = {} + + for part in msg.parts: + output = part.output or {} + + if part.type == PartType.MESSAGE: + content = output.get("content") + if content and isinstance(content, list) and len(content) > 0: + parts_data.append(TextPartVercel(text=content[0].get("text", ""))) + elif part.type == PartType.REASONING: + parts_data.extend( + ReasoningPartVercel(text=s.get("text", "")) + for s in output.get("summary", []) ) - - message_data["parts"] = parts - message_data["annotations"] = annotations - - # If we encounter a user message with a non empty buffer we have to add a dummy ai message. - elif parts: - messages.append( - MessagesReadVercel( - id=uuid.uuid4(), - role="assistant", - createdAt=msg.creation_date, - content="", - parts=parts, - annotations=annotations, - ) + elif part.type == PartType.FUNCTION_CALL: + tc_id = output.get("call_id", "") + tool_name = output.get("name", "") + try: + input_data = json.loads(output.get("arguments", "{}")) + except json.JSONDecodeError: + input_data = {} + tool_part = ToolCallPartVercel( + type=f"tool-{tool_name}", + toolCallId=tc_id, + state="input-available", + input=input_data, ) + parts_data.append(tool_part) + tool_calls[tc_id] = tool_part + + requires_validation = tool_hil_mapping.get(tool_name, False) - parts = [] - annotations = [] - messages.append(MessagesReadVercel(**message_data)) - - # Buffer tool calls until the next AI_MESSAGE - elif msg.entity == Entity.AI_TOOL: - content = json.loads(msg.content) - text_content = content.get("content") - reasoning_content = content.get("reasoning") - - # Add optional reasoning - if reasoning_content: - parts.append(ReasoningPartVercel(reasoning=reasoning_content)) - - for tc in msg.tool_calls: - requires_validation = tool_hil_mapping.get(tc.name, False) - if tc.validated is True: - status = "accepted" - elif tc.validated is False: + if part.validated is True: + status: Literal[ + "accepted", "rejected", "not_required", "pending" + ] = "accepted" + elif part.validated is False: status = "rejected" elif not requires_validation: status = "not_required" else: status = "pending" - parts.append(TextPartVercel(text=text_content or "")) - parts.append( - ToolCallPartVercel( - toolInvocation=ToolCallVercel( - toolCallId=tc.tool_call_id, - toolName=tc.name, - args=json.loads(tc.arguments), - state="call", - ) - ) + metadata[tc_id] = MetadataToolCallVercel( + toolCallId=tc_id, + validated=status, + isComplete=True if requires_validation else part.is_complete, ) - annotations.append( - AnnotationToolCallVercel( - toolCallId=tc.tool_call_id, - validated=status, # type: ignore - isComplete=msg.is_complete, - ) - ) - - # Merge the actual tool result back into the buffered part - elif msg.entity == Entity.TOOL: - tool_call_id = json.loads(msg.content).get("tool_call_id") - tool_call = next( - ( - part.toolInvocation - for part in parts - if isinstance(part, ToolCallPartVercel) - and part.toolInvocation.toolCallId == tool_call_id - ), - None, - ) - annotation = next( - ( - annotation - for annotation in annotations - if isinstance(annotation, AnnotationToolCallVercel) - and annotation.toolCallId == tool_call_id - ), - None, - ) - if tool_call: - tool_call.result = json.loads(msg.content).get("content") - tool_call.state = "result" - - if annotation: - annotation.isComplete = msg.is_complete - - # If the tool call buffer is not empty, we need to add a dummy AI message. - if parts: - messages.append( - MessagesReadVercel( - id=uuid.uuid4(), - role="assistant", - createdAt=msg.creation_date, - content="", - parts=parts, - annotations=annotations, - ) + elif part.type == PartType.FUNCTION_CALL_OUTPUT: + tc_id = output.get("call_id", "") + if tc_id in tool_calls: + tool_calls[tc_id].state = "output-available" + tool_calls[tc_id].output = output.get("output") or "{}" + metadata[tc_id].isComplete = part.is_complete + + is_complete = all(part.is_complete for part in msg.parts) if msg.parts else True + + msg_vercel = MessagesReadVercel( + id=msg.message_id, + role="user" if msg.entity == Entity.USER else "assistant", + createdAt=msg.creation_date, + isComplete=is_complete, + parts=parts_data, + metadata=ToolMetadataDict(toolCalls=list(metadata.values())) + if metadata + else None, ) - - # Reverse back to descending order and build next_cursor - ordered_messages = list(reversed(messages)) - next_cursor = db_messages[-1].creation_date if has_more else None + messages.append(msg_vercel) return PaginatedResponse( - next_cursor=next_cursor, + next_cursor=db_messages[-1].creation_date if has_more else None, has_more=has_more, page_size=page_size, - results=ordered_messages, + results=messages, ) @@ -505,10 +427,13 @@ async def filter_tools_and_model_by_conversation( openai_messages = await messages_to_openai_content(messages) - # Remove the content of tool responses to save tokens - for message in openai_messages: - if message["role"] == "tool": - message["content"] = "..." + filtered_messages = [] + for msg in openai_messages: + if msg.get("type") == PartType.REASONING.value: + continue + if msg.get("type") == PartType.FUNCTION_CALL_OUTPUT.value: + msg["output"] = "..." + filtered_messages.append(msg) # Build system prompt conditionally instructions = [] @@ -578,15 +503,20 @@ async def filter_tools_and_model_by_conversation( # Send the OpenAI request model = "google/gemini-2.5-flash" start_request = time.time() - response = await openai_client.beta.chat.completions.parse( - messages=[{"role": "system", "content": system_prompt}, *openai_messages], # type: ignore + response = await openai_client.responses.parse( + input=[{"role": "system", "content": system_prompt}, *filtered_messages], # type: ignore model=model, - response_format=ToolModelFiltering, + text_format=ToolModelFiltering, + store=False, ) # Parse the output - if response.choices[0].message.parsed: - parsed = response.choices[0].message.parsed + parsed = response.output_parsed + if parsed and response.output_parsed.selected_tools: + selected_tools = list(set(response.output_parsed.selected_tools)) + logger.debug( + f"#TOOLS: {len(selected_tools)}, SELECTED TOOLS: {selected_tools} in {(time.time() - start_request):.2f} s" + ) # Handle tool selection if need_tool_selection: diff --git a/backend/src/neuroagent/app/database/sql_schemas.py b/backend/src/neuroagent/app/database/sql_schemas.py index bf995b1ee..91d6e3ad7 100644 --- a/backend/src/neuroagent/app/database/sql_schemas.py +++ b/backend/src/neuroagent/app/database/sql_schemas.py @@ -3,6 +3,7 @@ import datetime import enum import uuid +from typing import Any from sqlalchemy import ( UUID, @@ -14,7 +15,7 @@ Integer, String, ) -from sqlalchemy.dialects.postgresql import TSVECTOR +from sqlalchemy.dialects.postgresql import JSONB, TSVECTOR from sqlalchemy.ext.asyncio import AsyncAttrs from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship @@ -25,12 +26,19 @@ def utc_now() -> datetime.datetime: class Entity(enum.Enum): - """Calss to restrict entity collumn.""" + """Class to restrict entity column.""" USER = "user" - AI_TOOL = "ai_tool" - TOOL = "tool" - AI_MESSAGE = "ai_message" + ASSISTANT = "assistant" + + +class PartType(enum.Enum): + """Type of Response API part.""" + + MESSAGE = "message" + REASONING = "reasoning" + FUNCTION_CALL = "function_call" + FUNCTION_CALL_OUTPUT = "function_call_output" class Task(enum.Enum): @@ -94,7 +102,7 @@ class Threads(Base): class Messages(Base): - """SQL table for the messsages in the threads.""" + """SQL table for user messages. Each message groups all AI responses/tool calls.""" __tablename__ = "messages" message_id: Mapped[uuid.UUID] = mapped_column( @@ -104,15 +112,17 @@ class Messages(Base): DateTime(timezone=True), default=utc_now ) entity: Mapped[Entity] = mapped_column(Enum(Entity), nullable=False) - content: Mapped[str] = mapped_column(String, nullable=False) - is_complete: Mapped[bool] = mapped_column(Boolean) + search_vector: Mapped[str | None] = mapped_column(TSVECTOR, nullable=True) thread_id: Mapped[uuid.UUID] = mapped_column( UUID, ForeignKey("threads.thread_id"), nullable=False ) thread: Mapped[Threads] = relationship("Threads", back_populates="messages") - tool_calls: Mapped[list["ToolCalls"]] = relationship( - "ToolCalls", back_populates="message", cascade="all, delete-orphan" + parts: Mapped[list["Parts"]] = relationship( + "Parts", + back_populates="message", + order_by="Parts.order_index", + cascade="all, delete-orphan", ) tool_selection: Mapped[list["ToolSelection"]] = relationship( "ToolSelection", cascade="all, delete-orphan" @@ -123,27 +133,27 @@ class Messages(Base): token_consumption: Mapped[list["TokenConsumption"]] = relationship( "TokenConsumption", cascade="all, delete-orphan" ) - search_vector: Mapped[str] = mapped_column(TSVECTOR, nullable=True) - - __table_args__ = ( - # GIN index for full-text search performance - Index("ix_messages_search_vector", "search_vector", postgresql_using="gin"), - ) - -class ToolCalls(Base): - """SQL table used for tool call parameters.""" - __tablename__ = "tool_calls" - tool_call_id: Mapped[str] = mapped_column(String, primary_key=True) - name: Mapped[str] = mapped_column(String, nullable=False) - arguments: Mapped[str] = mapped_column(String, nullable=False) - validated: Mapped[bool] = mapped_column(Boolean, nullable=True) +class Parts(Base): + """SQL table for storing Response API parts (JSONB format).""" + __tablename__ = "parts" + part_id: Mapped[uuid.UUID] = mapped_column( + UUID, primary_key=True, default=lambda: uuid.uuid4() + ) message_id: Mapped[uuid.UUID] = mapped_column( - UUID, ForeignKey("messages.message_id") + UUID, ForeignKey("messages.message_id"), nullable=False ) - message: Mapped[Messages] = relationship("Messages", back_populates="tool_calls") + order_index: Mapped[int] = mapped_column(Integer, nullable=False) + type: Mapped[PartType] = mapped_column(Enum(PartType), nullable=False) + output: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False) + is_complete: Mapped[bool] = mapped_column(Boolean, nullable=False) + validated: Mapped[bool] = mapped_column(Boolean, nullable=True) + + message: Mapped[Messages] = relationship("Messages", back_populates="parts") + + __table_args__ = (Index("ix_parts_message_id", "message_id"),) class ToolSelection(Base): diff --git a/backend/src/neuroagent/app/dependencies.py b/backend/src/neuroagent/app/dependencies.py index 3dbd2e836..5b368b7e5 100644 --- a/backend/src/neuroagent/app/dependencies.py +++ b/backend/src/neuroagent/app/dependencies.py @@ -1,12 +1,12 @@ """App dependencies.""" -import json import logging import re from datetime import datetime, timezone from functools import cache from pathlib import Path from typing import Annotated, Any, AsyncIterator +from uuid import UUID import boto3 from fastapi import Depends, HTTPException, Request @@ -25,7 +25,13 @@ validate_project, ) from neuroagent.app.config import Settings -from neuroagent.app.database.sql_schemas import Entity, Messages, Threads +from neuroagent.app.database.sql_schemas import ( + Entity, + Messages, + Parts, + PartType, + Threads, +) from neuroagent.app.schemas import OpenRouterModelResponse, UserInfo from neuroagent.executor import WasmExecutor from neuroagent.mcp import MCPClient, create_dynamic_tool @@ -111,6 +117,7 @@ StrainGetOneTool, SubjectGetAllTool, SubjectGetOneTool, + WeatherTool, WebSearchTool, ) from neuroagent.tools.base_tool import BaseTool @@ -266,7 +273,7 @@ async def get_user_info( async def get_thread( user_info: Annotated[UserInfo, Depends(get_user_info)], - thread_id: str, + thread_id: UUID, session: Annotated[AsyncSession, Depends(get_session)], ) -> Threads: """Check if the current thread / user matches.""" @@ -467,7 +474,8 @@ def get_tool_list( RunPythonTool, WebSearchTool, # NowTool, - # WeatherTool, + WeatherTool, + # RandomPlotGeneratorTool, ] all_tools = internal_tool_list + mcp_tool_list @@ -520,19 +528,36 @@ async def filtered_tools( body = await request.json() - # Awaiting here makes downstream calls already loaded so no performance issue messages: list[Messages] = await thread.awaitable_attrs.messages + + for message in messages: + await message.awaitable_attrs.parts + if ( not messages - or messages[-1].entity == Entity.AI_MESSAGE - or not messages[-1].is_complete + or not messages[-1].parts + or messages[-1].parts[-1].type == PartType.MESSAGE + or not messages[-1].parts[-1].is_complete ): messages.append( Messages( thread_id=thread.thread_id, entity=Entity.USER, - content=json.dumps({"role": "user", "content": body["content"]}), - is_complete=True, + parts=[ + Parts( + order_index=0, + type=PartType.MESSAGE, + output={ + "type": "message", + "role": "user", + "content": [ + {"type": "input_text", "text": body["content"]} + ], + "status": "completed", + }, + is_complete=True, + ) + ], ) ) @@ -565,14 +590,14 @@ async def filtered_tools( last_user_message = next( message for message in reversed(messages) if message.entity == Entity.USER ) - previously_selected_tools = [ - selected.tool_name - for selected in await last_user_message.awaitable_attrs.tool_selection - ] + tool_selection = await last_user_message.awaitable_attrs.tool_selection + model_selection = await last_user_message.awaitable_attrs.model_selection + + previously_selected_tools = [selected.tool_name for selected in tool_selection] previous_model_and_reasoning: dict[str, str | None] = { - "model": last_user_message.model_selection.model, - "reasoning": last_user_message.model_selection.reasoning.value - if last_user_message.model_selection.reasoning + "model": model_selection.model, + "reasoning": model_selection.reasoning.value + if model_selection.reasoning else None, } return [ diff --git a/backend/src/neuroagent/app/routers/qa.py b/backend/src/neuroagent/app/routers/qa.py index 72f4ef464..8c1039a8f 100644 --- a/backend/src/neuroagent/app/routers/qa.py +++ b/backend/src/neuroagent/app/routers/qa.py @@ -19,8 +19,9 @@ from obp_accounting_sdk.constants import ServiceSubtype from openai import AsyncOpenAI from redis import asyncio as aioredis -from sqlalchemy import or_, select +from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.orm import selectinload from neuroagent.agent_routine import AgentsRoutine from neuroagent.app.app_utils import ( @@ -29,7 +30,7 @@ validate_project, ) from neuroagent.app.config import Settings -from neuroagent.app.database.sql_schemas import Entity, Messages, Threads +from neuroagent.app.database.sql_schemas import Messages, Threads from neuroagent.app.dependencies import ( get_accounting_session_factory, get_agents_routine, @@ -132,16 +133,11 @@ async def question_suggestions( # Get the AI and User messages from the conversation : messages_result = await session.execute( select(Messages) - .where( - Messages.thread_id == thread.thread_id, - or_( - Messages.entity == Entity.USER, - Messages.entity == Entity.AI_MESSAGE, - ), - ) + .options(selectinload(Messages.parts)) + .where(Messages.thread_id == thread.thread_id) .order_by(Messages.creation_date) ) - db_messages = messages_result.unique().scalars().all() + db_messages = messages_result.scalars().all() is_in_chat = bool(db_messages) if not is_in_chat and not body.click_history: @@ -151,14 +147,23 @@ async def question_suggestions( ) if is_in_chat: - content = f"CONVERSATION MESSAGES: \n{json.dumps([{k: v for k, v in json.loads(msg.content).items() if k in ['role', 'content']} for msg in db_messages])}" + messages_str = "\n".join( + [ + json.dumps( + { + "entity": msg.entity.value, + "text": msg.parts[-1].output.get("content", {})[0].get("text"), + } + ) + for msg in db_messages + if msg.parts + ] + ) + content = f"CONVERSATION MESSAGES: \n{messages_str}" else: content = f"USER JOURNEY: \n{body.model_dump(exclude={'thread_id'}, mode='json')['click_history']}" - messages = [ - { - "role": "system", - "content": f"""You are a smart assistant that analyzes user behavior and conversation history to suggest {"three concise, engaging questions" if is_in_chat else "a concise, engaging question"} the user might ask next, specifically about finding relevant scientific literature. + system_prompt = f"""You are a smart assistant that analyzes user behavior and conversation history to suggest {"three concise, engaging questions" if is_in_chat else "a concise, engaging question"} the user might ask next, specifically about finding relevant scientific literature. Platform Context: The Open Brain Platform provides an atlas-driven exploration of the mouse brain, offering access to: @@ -202,20 +207,22 @@ async def question_suggestions( The upcoming user message will either prepend its content with 'CONVERSATION MESSAGES:' indicating that messages from the conversation are dumped, or 'USER JOURNEY:' indicating that the navigation history is dumped. -Important: Weight the user clicks depending on how old they are. The more recent clicks should be given a higher importance. The current date and time is {datetime.now(timezone.utc).isoformat()}.""", - }, - {"role": "user", "content": content}, - ] +Important: Weight the user clicks depending on how old they are. The more recent clicks should be given a higher importance. The current date and time is {datetime.now(timezone.utc).isoformat()}.""" - response = await openai_client.beta.chat.completions.parse( - messages=messages, # type: ignore + response = await openai_client.responses.parse( + instructions=system_prompt, + input=content, model=settings.llm.suggestion_model, - response_format=QuestionsSuggestions + text_format=QuestionsSuggestions if is_in_chat else QuestionSuggestionNoMessages, + store=False, ) - return response.choices[0].message.parsed # type: ignore + if response.output_parsed: + return response.output_parsed # type: ignore + else: + raise ValueError("Error generating question suggestions.") @router.get("/models") @@ -286,8 +293,9 @@ async def stream_chat_agent( # No need to await since it has been awaited in tool filtering dependency messages: list[Messages] = thread.messages - + # Add background task to commit messages after streaming / stop background_tasks.add_task(commit_messages, session, messages, thread) + async with accounting_context( subtype=ServiceSubtype.ML_LLM, user_id=thread.user_id, @@ -305,7 +313,7 @@ async def stream_chat_agent( stream_generator, media_type="text/event-stream", headers={ - "x-vercel-ai-data-stream": "v1", + "x-vercel-ai-ui-message-stream": "v1", "Access-Control-Expose-Headers": ",".join( list(limit_headers.model_dump(by_alias=True).keys()) ), diff --git a/backend/src/neuroagent/app/routers/threads.py b/backend/src/neuroagent/app/routers/threads.py index 6fda579e7..cbabaf6e7 100644 --- a/backend/src/neuroagent/app/routers/threads.py +++ b/backend/src/neuroagent/app/routers/threads.py @@ -1,6 +1,5 @@ """Threads CRUDs.""" -import json import logging from typing import Annotated, Any, Literal from uuid import UUID @@ -9,7 +8,7 @@ from openai import AsyncOpenAI from pydantic import AwareDatetime from redis import asyncio as aioredis -from sqlalchemy import desc, exists, func, or_, select, true +from sqlalchemy import desc, exists, func, or_, select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import selectinload @@ -20,7 +19,7 @@ validate_project, ) from neuroagent.app.config import Settings -from neuroagent.app.database.sql_schemas import Entity, Messages, Threads, utc_now +from neuroagent.app.database.sql_schemas import Messages, Threads, utc_now from neuroagent.app.dependencies import ( get_openai_client, get_redis_client, @@ -98,19 +97,13 @@ async def search( search_query = func.plainto_tsquery("english", query) sql_query = ( - select( - Messages.thread_id, - Messages.message_id, - Threads.title, - Messages.content, - ) - .select_from(Messages) + select(Messages) + .options(selectinload(Messages.parts), selectinload(Messages.thread)) .join(Threads, Messages.thread_id == Threads.thread_id) .where( Threads.user_id == user_info.sub, Threads.vlab_id == virtual_lab_id, Threads.project_id == project_id, - Messages.entity.in_(["USER", "AI_MESSAGE"]), Messages.search_vector.op("@@")(search_query), ) .distinct(Messages.thread_id) @@ -123,16 +116,16 @@ async def search( ) result = await session.execute(sql_query) - results = result.fetchall() + messages = result.scalars().all() return SearchMessagesList( result_list=[ SearchMessagesResult( - thread_id=result[0], - message_id=result[1], - title=result[2], - content=json.loads(result[3])["content"], + thread_id=msg.thread_id, + message_id=msg.message_id, + title=msg.thread.title, + content=msg.parts[-1].output.get("content", {})[0].get("text"), ) - for result in results + for msg in messages ] ) @@ -175,22 +168,21 @@ async def generate_title( } ) # Send it to OpenAI longside with the system prompt asking for summary - messages = [ - { - "role": "system", - "content": "Given the user's first message of a conversation, generate a short title for this conversation (max 5 words).", - }, - {"role": "user", "content": body.first_user_message}, - ] + system_prompt = "Given the user's first message of a conversation, generate a short title for this conversation (max 5 words)." - response = await openai_client.beta.chat.completions.parse( - messages=messages, # type: ignore + response = await openai_client.responses.parse( + instructions=system_prompt, + input=body.first_user_message, model=settings.llm.suggestion_model, - response_format=ThreadGeneratedTitle, + text_format=ThreadGeneratedTitle, + store=False, ) # Update the thread title and modified date + commit - thread.title = response.choices[0].message.parsed.title # type: ignore + if response.output_parsed: + thread.title = response.output_parsed.title + else: + logger.warning("Unable to generate title.") thread.update_date = utc_now() await session.commit() await session.refresh(thread) @@ -318,109 +310,53 @@ async def get_thread_by_id( @router.get("/{thread_id}/messages") async def get_thread_messages( session: Annotated[AsyncSession, Depends(get_session)], - _: Annotated[Threads, Depends(get_thread)], # to check if thread exists + _: Annotated[Threads, Depends(get_thread)], thread_id: str, tool_list: Annotated[list[type[BaseTool]], Depends(get_tool_list)], pagination_params: PaginatedParams = Depends(), - entity: list[Literal["USER", "AI_TOOL", "TOOL", "AI_MESSAGE"]] | None = Query( - default=None - ), + entity: list[Literal["USER", "ASSISTANT"]] | None = Query(default=None), sort: Literal["creation_date", "-creation_date"] = "-creation_date", - vercel_format: bool = Query(default=False), + vercel_format: bool = False, ) -> PaginatedResponse[MessagesRead] | PaginatedResponse[MessagesReadVercel]: """Get all messages of the thread.""" - # Create mapping of tool names to their HIL requirement tool_hil_mapping = {tool.name: tool.hil for tool in tool_list} - if vercel_format: - entity = ["USER", "AI_MESSAGE"] - + where_conditions = [Messages.thread_id == thread_id] if entity: - entity_where = or_(*[Messages.entity == ent for ent in entity]) - else: - entity_where = true() - - where_conditions = [Messages.thread_id == thread_id, entity_where] - - if pagination_params.cursor is not None: - comparison_op = ( + where_conditions.append(or_(*[Messages.entity == ent for ent in entity])) + if pagination_params.cursor: + where_conditions.append( Messages.creation_date < pagination_params.cursor - if (sort.startswith("-") or vercel_format) + if sort.startswith("-") else Messages.creation_date > pagination_params.cursor ) - where_conditions.append(comparison_op) - # Only get the relevent info for output format, we will then make the full query after. - messages_result = await session.execute( - select(Messages.message_id, Messages.creation_date, Messages.entity) + result = await session.execute( + select(Messages) + .options(selectinload(Messages.parts)) .where(*where_conditions) .order_by( desc(Messages.creation_date) - if (sort.startswith("-") or vercel_format) + if sort.startswith("-") else Messages.creation_date ) .limit(pagination_params.page_size + 1) ) - # This is a list of tuples with (message_id, creation_date, entitty) - db_cursor = messages_result.all() - - if not db_cursor: - return PaginatedResponse( - next_cursor=None, - has_more=False, - page_size=pagination_params.page_size, - results=[], - ) + db_messages = result.scalars().all() - has_more = len(db_cursor) > pagination_params.page_size - if not vercel_format and has_more: - db_cursor = db_cursor[:-1] + has_more = len(db_messages) > pagination_params.page_size + db_messages = db_messages[:-1] if has_more else db_messages - if vercel_format: - # We set the most recent boudary to the cursor if it exists. - date_conditions = ( - [(Messages.creation_date < pagination_params.cursor)] - if pagination_params.cursor - else [] - ) - - # If there are more messages we set the oldest bound for the messages. - if has_more: - if db_cursor[-2][2] == Entity.USER: - date_conditions.append(Messages.creation_date >= db_cursor[-2][1]) - else: - date_conditions.append(Messages.creation_date > db_cursor[-1][1]) - # This is a trick to include all tool from last AI. - - # Get all messages in the date frame. - all_msg_in_page_query = ( - select(Messages) - .options(selectinload(Messages.tool_calls)) - .where(Messages.thread_id == thread_id, *date_conditions) - .order_by(desc(Messages.creation_date)) - ) - all_msg_in_page_result = await session.execute(all_msg_in_page_query) - db_messages = all_msg_in_page_result.scalars().all() - else: - # Here we simply get all messages with the ID found before. - # Pagination needs to happen on non-joined parent. - # Once we have them we can eager load the tool calls - complete_messages_results = await session.execute( - select(Messages) - .options(selectinload(Messages.tool_calls)) - .where(Messages.message_id.in_([msg[0] for msg in db_cursor])) - .order_by( - desc(Messages.creation_date) - if sort.startswith("-") - else Messages.creation_date - ) - ) - db_messages = complete_messages_results.scalars().all() if vercel_format: return format_messages_vercel( - db_messages, tool_hil_mapping, has_more, pagination_params.page_size + db_messages, + tool_hil_mapping, + has_more, + pagination_params.page_size, ) else: return format_messages_output( - db_messages, tool_hil_mapping, has_more, pagination_params.page_size + db_messages, + has_more, + pagination_params.page_size, ) diff --git a/backend/src/neuroagent/app/routers/tools.py b/backend/src/neuroagent/app/routers/tools.py index 14fdd06c0..8be5d3cca 100644 --- a/backend/src/neuroagent/app/routers/tools.py +++ b/backend/src/neuroagent/app/routers/tools.py @@ -4,14 +4,17 @@ import json import logging from typing import Annotated, Any +from uuid import UUID from fastapi import APIRouter, Depends, HTTPException +from openai.types.responses import ResponseFunctionToolCall from pydantic import ValidationError from pydantic.json_schema import SkipJsonSchema +from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from neuroagent.agent_routine import AgentsRoutine -from neuroagent.app.database.sql_schemas import Entity, Messages, Threads, ToolCalls +from neuroagent.app.database.sql_schemas import Parts, PartType, Threads from neuroagent.app.dependencies import ( get_agents_routine, get_context_variables, @@ -37,70 +40,89 @@ @router.patch("/{thread_id}/execute/{tool_call_id}") async def execute_tool_call( - thread_id: str, + thread_id: UUID, tool_call_id: str, request: ExecuteToolCallRequest, - _: Annotated[Threads, Depends(get_thread)], # validates thread belongs to user + _: Annotated[Threads, Depends(get_thread)], session: Annotated[AsyncSession, Depends(get_session)], tool_list: Annotated[list[type[BaseTool]], Depends(get_tool_list)], context_variables: Annotated[dict[str, Any], Depends(get_context_variables)], agents_routine: Annotated[AgentsRoutine, Depends(get_agents_routine)], ) -> ExecuteToolCallResponse: """Execute a specific tool call and update its status.""" - # Get the tool call - tool_call = await session.get(ToolCalls, tool_call_id) - if not tool_call: - raise HTTPException(status_code=404, detail="Specified tool call not found.") - - # Check if tool call has already been validated - if tool_call.validated is not None: - raise HTTPException( - status_code=403, - detail="The tool call has already been validated.", + # Find the part containing this tool call + result = await session.execute( + select(Parts).where( + Parts.type == PartType.FUNCTION_CALL, + Parts.output["call_id"].astext == tool_call_id, ) + ) + part = result.scalars().first() + if not part: + raise HTTPException(status_code=404, detail="Tool call not found.") + + # Check if already validated + if part.validated is not None: + raise HTTPException(status_code=403, detail="Tool call already validated.") + + # Get the next order index + next_order_result = await session.execute( + select(func.max(Parts.order_index) + 1).where( + Parts.message_id == part.message_id + ) + ) + next_order_index = next_order_result.scalar() - # Update tool call validation status - tool_call.validated = request.validation == "accepted" - - # Update arguments if provided and accepted - if request.args and request.validation == "accepted": - tool_call.arguments = request.args + # Update validation status + part.validated = request.validation == "accepted" - # Handle rejection case if request.validation == "rejected": - message = { - "role": "tool", - "tool_call_id": tool_call.tool_call_id, - "tool_name": tool_call.name, - "content": f"Tool call refused by the user. User's feedback: {request.feedback}" + # Create rejection output + output_content = ( + f"Tool call refused by the user. User's feedback: {request.feedback}" if request.feedback - else "This tool call has been refused by the user. DO NOT re-run it unless explicitly asked by the user.", + else "This tool call has been refused by the user. DO NOT re-run it unless explicitly asked by the user." + ) + tool_output = { + "id": part.output["id"], + "call_id": part.output["call_id"], + "output": output_content, + "type": "function_call_output", + "status": "incomplete", } - else: # Handle acceptance case + else: + # Execute the tool + tool_call_obj = ResponseFunctionToolCall( + id=part.output["id"], + call_id=part.output["call_id"], + name=part.output["name"], + arguments=part.output["arguments"], + type="function_call", + status="completed", + ) try: - message, _ = await agents_routine.handle_tool_call( - tool_call=tool_call, + tool_output_obj, _ = await agents_routine.handle_tool_call( + tool_call=tool_call_obj, tools=tool_list, context_variables=context_variables, raise_validation_errors=True, ) - except ValidationError: - # Return early with validation-error status without committing to DB - return ExecuteToolCallResponse(status="validation-error", content=None) - - # Add the tool response as a new message - new_message = Messages( - thread_id=thread_id, - entity=Entity.TOOL, - content=json.dumps(message), + tool_output = tool_output_obj.model_dump() + except ValidationError as e: + return ExecuteToolCallResponse(status="validation-error", content=str(e)) + + # Add output as new part + new_part = Parts( + message_id=part.message_id, + order_index=next_order_index, + type=PartType.FUNCTION_CALL_OUTPUT, + output=tool_output, is_complete=True, ) - - session.add(tool_call) - session.add(new_message) + session.add(new_part) await session.commit() - return ExecuteToolCallResponse(status="done", content=message["content"]) + return ExecuteToolCallResponse(status="done", content=tool_output["output"]) @router.get("") diff --git a/backend/src/neuroagent/app/schemas.py b/backend/src/neuroagent/app/schemas.py index c1347ec7f..fabbd33d7 100644 --- a/backend/src/neuroagent/app/schemas.py +++ b/backend/src/neuroagent/app/schemas.py @@ -7,25 +7,21 @@ from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, conlist -class ToolCallVercel(BaseModel): +class ToolCallPartVercel(BaseModel): """Tool call in Vercel format.""" + # The tool name is included in the type: 'tool-{you_name}'" + type: str = Field(pattern=r"^tool-.+$") toolCallId: str - toolName: str - args: dict[str, Any] - state: Literal["partial-call", "call", "result"] - result: str | None = None + state: Literal[ + "input-streaming", "input-available", "output-available", "output-error" + ] + input: dict[str, Any] + output: str | None = None model_config = ConfigDict(extra="ignore") -class ToolCallPartVercel(BaseModel): - """Tool call part from Vercel.""" - - type: Literal["tool-invocation"] = "tool-invocation" - toolInvocation: ToolCallVercel - - class TextPartVercel(BaseModel): """Text part of Vercel.""" @@ -37,17 +33,10 @@ class ReasoningPartVercel(BaseModel): """Text part of Vercel.""" type: Literal["reasoning"] = "reasoning" - reasoning: str - - -class AnnotationMessageVercel(BaseModel): - """Annotation of vercel messages.""" - - messageId: UUID - isComplete: bool + text: str -class AnnotationToolCallVercel(BaseModel): +class MetadataToolCallVercel(BaseModel): """Annotation of vercel tool calls.""" toolCallId: str @@ -55,6 +44,12 @@ class AnnotationToolCallVercel(BaseModel): isComplete: bool +class ToolMetadataDict(BaseModel): + """Dict for HIL Annotations.""" + + toolCalls: list[MetadataToolCallVercel] + + class ToolCall(BaseModel): """Tool call.""" @@ -77,9 +72,9 @@ class MessagesReadVercel(BaseRead): id: UUID role: str createdAt: AwareDatetime - content: str + isComplete: bool parts: list[ToolCallPartVercel | TextPartVercel | ReasoningPartVercel] | None = None - annotations: list[AnnotationMessageVercel | AnnotationToolCallVercel] | None = None + metadata: ToolMetadataDict | None = None class MessagesRead(BaseRead): @@ -88,11 +83,9 @@ class MessagesRead(BaseRead): message_id: UUID entity: str thread_id: UUID - is_complete: bool creation_date: AwareDatetime - msg_content: dict[str, Any] + parts: list[dict[str, Any]] model: str | None = None - tool_calls: list[ToolCall] class ThreadsRead(BaseRead): @@ -145,8 +138,7 @@ class ExecuteToolCallRequest(BaseModel): """Request body for executing a tool call.""" validation: Literal["rejected", "accepted"] - args: str | None = None - feedback: str | None = None # For refusal + feedback: str | None = None class ExecuteToolCallResponse(BaseModel): @@ -237,7 +229,7 @@ class QuestionsSuggestionsRequest(BaseModel): """Request for the suggestion endpoint.""" click_history: list[UserJourney] | None = None - thread_id: str | None = None + thread_id: UUID | None = None class Architecture(BaseModel): diff --git a/backend/src/neuroagent/new_types.py b/backend/src/neuroagent/new_types.py index bcf795202..9f82bbc22 100644 --- a/backend/src/neuroagent/new_types.py +++ b/backend/src/neuroagent/new_types.py @@ -3,6 +3,7 @@ from typing import Any, Callable # Third-party imports +from openai.types.responses import ResponseFunctionToolCallOutputItem from pydantic import BaseModel, ConfigDict from neuroagent.tools.base_tool import BaseTool @@ -40,7 +41,7 @@ class HILValidation(BaseModel): class Response(BaseModel): """Agent response.""" - messages: list[dict[str, Any]] = [] + messages: list[ResponseFunctionToolCallOutputItem] = [] agent: Agent | None = None context_variables: dict[str, Any] = {} hil_messages: list[HILResponse] | None = None diff --git a/backend/src/neuroagent/scripts/evaluate_agent.py b/backend/src/neuroagent/scripts/evaluate_agent.py index 49f272412..345774b15 100644 --- a/backend/src/neuroagent/scripts/evaluate_agent.py +++ b/backend/src/neuroagent/scripts/evaluate_agent.py @@ -158,42 +158,33 @@ def parse_ai_sdk_streaming_response(streamed_data: str) -> dict[str, Any]: ] } """ - response_tokens = [] - tool_args_buffer: dict[str, str] = {} # toolCallId -> args string + response_tokens: list[str] = [] tool_calls = {} + current_text_id = None for line in streamed_data.splitlines(): - prefix, _, data = line.partition(":") + _, _, data = line.partition(":") try: content = json.loads(data) except json.JSONDecodeError: continue - # Streamed response text - if prefix == "0": - token = data.strip('"') - response_tokens.append(token) - - # Final tool call object (can override partials) - elif prefix == "9": - tool_call_id = content.get("toolCallId") - tool_calls[tool_call_id] = { - "name": content.get("toolName"), - "arguments": content.get("args", {}), + if content["type"] == "text-start": + # reset on new text part. We don't want all the text in between tool calls. + current_text_id = content["id"] + response_tokens = [] + elif content["type"] == "text-delta" and content["id"] == current_text_id: + response_tokens.append(content["delta"]) + elif content["type"] == "tool-input-available": + tool_calls[content["toolCallId"]] = { + "name": content["toolName"], + "arguments": content["input"], } - # Final pass: fill in any tool calls using streamed args - for tool_call_id, args_str in tool_args_buffer.items(): - if tool_call_id not in tool_calls: - tool_calls[tool_call_id] = {"name": None, "arguments": {}} - try: - tool_calls[tool_call_id]["arguments"] = json.loads(args_str) - except json.JSONDecodeError: - tool_calls[tool_call_id]["arguments"] = {} - - final_output = "".join(response_tokens).strip() - - return {"response": final_output, "tool_calls": list(tool_calls.values())} + return { + "response": "".join(response_tokens).strip(), + "tool_calls": list(tool_calls.values()), + } def filter_test_cases_by_pattern( diff --git a/backend/src/neuroagent/tools/autogenerated_types/entitycore.py b/backend/src/neuroagent/tools/autogenerated_types/entitycore.py index 71255e01c..dc7b9d771 100644 --- a/backend/src/neuroagent/tools/autogenerated_types/entitycore.py +++ b/backend/src/neuroagent/tools/autogenerated_types/entitycore.py @@ -55,30 +55,13 @@ class AgePeriod(RootModel[Literal['prenatal', 'postnatal', 'unknown']]): root: Literal['prenatal', 'postnatal', 'unknown'] = Field(..., title='AgePeriod') -class AnalysisNotebookExecutionCreate(BaseModel): - model_config = ConfigDict( - extra='allow', - ) - authorized_public: bool = Field(default=False, title='Authorized Public') - start_time: AwareDatetime | None = Field(default=None, title='Start Time') - end_time: AwareDatetime | None = Field(default=None, title='End Time') - used_ids: list[UUID] = Field(default=[], title='Used Ids') - generated_ids: list[UUID] = Field(default=[], title='Generated Ids') - analysis_notebook_template_id: UUID | None = Field( - default=None, title='Analysis Notebook Template Id' - ) - analysis_notebook_environment_id: UUID = Field( - ..., title='Analysis Notebook Environment Id' - ) - - class AnalysisNotebookResultCreate(BaseModel): model_config = ConfigDict( extra='allow', ) - authorized_public: bool = Field(default=False, title='Authorized Public') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + authorized_public: bool = Field(default=False, title='Authorized Public') class AnalysisNotebookResultUpdate(BaseModel): @@ -410,9 +393,9 @@ class CircuitExtractionCampaignCreate(BaseModel): model_config = ConfigDict( extra='allow', ) - authorized_public: bool = Field(default=False, title='Authorized Public') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + authorized_public: bool = Field(default=False, title='Authorized Public') scan_parameters: dict[str, Any] = Field(..., title='Scan Parameters') @@ -431,9 +414,9 @@ class CircuitExtractionConfigCreate(BaseModel): model_config = ConfigDict( extra='allow', ) - authorized_public: bool = Field(default=False, title='Authorized Public') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + authorized_public: bool = Field(default=False, title='Authorized Public') circuit_id: UUID = Field(..., title='Circuit Id') scan_parameters: dict[str, Any] = Field(..., title='Scan Parameters') @@ -481,6 +464,8 @@ class CircuitUserUpdate(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str | None = Field(default='', title='Name') + description: str | None = Field(default='', title='Description') license_id: UUID | str | None = Field(default='', title='License Id') brain_region_id: UUID | str | None = Field( default='', title='Brain Region Id' @@ -492,8 +477,6 @@ class CircuitUserUpdate(BaseModel): contact_email: str | None = Field(default='', title='Contact Email') published_in: str | None = Field(default='', title='Published In') notice_text: str | None = Field(default='', title='Notice Text') - name: str | None = Field(default='', title='Name') - description: str | None = Field(default='', title='Description') has_morphologies: bool | str | None = Field( default='', title='Has Morphologies' ) @@ -535,6 +518,8 @@ class ComputationallySynthesizedCellMorphologyProtocolCreate(BaseModel): default=None, title='Protocol Document' ) protocol_design: CellMorphologyProtocolDesign + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_public: bool = Field(default=False, title='Authorized Public') type: Literal['cell_morphology_protocol'] = Field( default='cell_morphology_protocol', title='Type' @@ -766,9 +751,9 @@ class EModelCreate(BaseModel): model_config = ConfigDict( extra='allow', ) - authorized_public: bool = Field(default=False, title='Authorized Public') - description: str = Field(..., title='Description') name: str = Field(..., title='Name') + description: str = Field(..., title='Description') + authorized_public: bool = Field(default=False, title='Authorized Public') iteration: str = Field(..., title='Iteration') score: float = Field(..., title='Score') seed: int = Field(..., title='Seed') @@ -782,8 +767,8 @@ class EModelUserUpdate(BaseModel): model_config = ConfigDict( extra='allow', ) - description: str | None = Field(default='', title='Description') name: str | None = Field(default='', title='Name') + description: str | None = Field(default='', title='Description') iteration: str | None = Field(default='', title='Iteration') score: float | str | None = Field(default='', title='Score') seed: int | str | None = Field(default='', title='Seed') @@ -1135,6 +1120,14 @@ class ErrorResponse(BaseModel): details: Any | None = Field(default=None, title='Details') +class ExecutorType( + RootModel[Literal['single_node_job', 'distributed_job', 'jupyter_notebook']] +): + root: Literal['single_node_job', 'distributed_job', 'jupyter_notebook'] = Field( + ..., title='ExecutorType' + ) + + class ExternalSource(RootModel[Literal['channelpedia', 'modeldb', 'icgenealogy']]): root: Literal['channelpedia', 'modeldb', 'icgenealogy'] = Field( ..., @@ -1147,10 +1140,10 @@ class ExternalUrlCreate(BaseModel): model_config = ConfigDict( extra='allow', ) - source: ExternalSource - url: AnyUrl = Field(..., title='Url') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + source: ExternalSource + url: AnyUrl = Field(..., title='Url') class Facet(BaseModel): @@ -1219,9 +1212,9 @@ class IonChannelModelingConfigCreate(BaseModel): model_config = ConfigDict( extra='allow', ) - authorized_public: bool = Field(default=False, title='Authorized Public') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + authorized_public: bool = Field(default=False, title='Authorized Public') ion_channel_modeling_campaign_id: UUID = Field( ..., title='Ion Channel Modeling Campaign Id' ) @@ -1257,6 +1250,8 @@ class IonChannelRecordingCreate(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_public: bool = Field(default=False, title='Authorized Public') license_id: UUID | None = Field(default=None, title='License Id') brain_region_id: UUID = Field(..., title='Brain Region Id') @@ -1281,8 +1276,6 @@ class IonChannelRecordingCreate(BaseModel): description='Text provided by the data creators to inform users about data caveats, limitations, or required attribution practices.', title='Notice Text', ) - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') ljp: float = Field( default=0.0, description='Correction applied to the voltage trace, in mV', @@ -1328,6 +1321,8 @@ class IonChannelRecordingUserUpdate(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str | None = Field(default='', title='Name') + description: str | None = Field(default='', title='Description') license_id: UUID | str | None = Field(default='', title='License Id') brain_region_id: UUID | str | None = Field( default='', title='Brain Region Id' @@ -1339,8 +1334,6 @@ class IonChannelRecordingUserUpdate(BaseModel): contact_email: str | None = Field(default='', title='Contact Email') published_in: str | None = Field(default='', title='Published In') notice_text: str | None = Field(default='', title='Notice Text') - name: str | None = Field(default='', title='Name') - description: str | None = Field(default='', title='Description') ljp: float | str | None = Field(default='', title='Ljp') recording_location: list[str] | str | None = Field( default='', title='Recording Location' @@ -1382,11 +1375,11 @@ class LicenseRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') id: UUID = Field(..., title='Id') creation_date: AwareDatetime = Field(..., title='Creation Date') update_date: AwareDatetime = Field(..., title='Update Date') - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') label: str = Field(..., title='Label') @@ -1488,6 +1481,8 @@ class ModifiedReconstructionCellMorphologyProtocolCreate(BaseModel): default=None, title='Protocol Document' ) protocol_design: CellMorphologyProtocolDesign + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_public: bool = Field(default=False, title='Authorized Public') type: Literal['cell_morphology_protocol'] = Field( default='cell_morphology_protocol', title='Type' @@ -1506,6 +1501,8 @@ class NestedComputationallySynthesizedCellMorphologyProtocolRead(BaseModel): default=None, title='Protocol Document' ) protocol_design: CellMorphologyProtocolDesign + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') id: UUID = Field(..., title='Id') type: Literal['cell_morphology_protocol'] = Field( default='cell_morphology_protocol', title='Type' @@ -1568,10 +1565,10 @@ class NestedElectricalRecordingStimulusRead(BaseModel): model_config = ConfigDict( extra='allow', ) - type: EntityType | None = None - id: UUID = Field(..., title='Id') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + type: EntityType | None = None + id: UUID = Field(..., title='Id') dt: float | None = Field(default=None, title='Dt') injection_type: ElectricalRecordingStimulusType shape: ElectricalRecordingStimulusShape @@ -1594,11 +1591,11 @@ class NestedExternalUrlRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') id: UUID = Field(..., title='Id') source: ExternalSource url: AnyUrl = Field(..., title='Url') - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') source_name: str = Field(..., title='Source Name') @@ -1606,10 +1603,10 @@ class NestedIonChannelModelingConfigRead(BaseModel): model_config = ConfigDict( extra='allow', ) - id: UUID = Field(..., title='Id') - type: EntityType | None = None name: str = Field(..., title='Name') description: str = Field(..., title='Description') + id: UUID = Field(..., title='Id') + type: EntityType | None = None ion_channel_modeling_campaign_id: UUID = Field( ..., title='Ion Channel Modeling Campaign Id' ) @@ -1620,9 +1617,9 @@ class NestedIonChannelRead(BaseModel): model_config = ConfigDict( extra='allow', ) - id: UUID = Field(..., title='Id') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + id: UUID = Field(..., title='Id') label: str = Field(..., title='Label') gene: str = Field(..., title='Gene') synonyms: list[str] = Field(..., title='Synonyms') @@ -1632,6 +1629,8 @@ class NestedIonChannelRecordingRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') type: EntityType | None = None @@ -1656,8 +1655,6 @@ class NestedIonChannelRecordingRead(BaseModel): description='Text provided by the data creators to inform users about data caveats, limitations, or required attribution practices.', title='Notice Text', ) - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') ljp: float = Field( default=0.0, description='Correction applied to the voltage trace, in mV', @@ -1702,6 +1699,8 @@ class NestedModifiedReconstructionCellMorphologyProtocolRead(BaseModel): default=None, title='Protocol Document' ) protocol_design: CellMorphologyProtocolDesign + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') id: UUID = Field(..., title='Id') type: Literal['cell_morphology_protocol'] = Field( default='cell_morphology_protocol', title='Type' @@ -1731,6 +1730,8 @@ class NestedPlaceholderCellMorphologyProtocolRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') id: UUID = Field(..., title='Id') type: Literal['cell_morphology_protocol'] = Field( default='cell_morphology_protocol', title='Type' @@ -1784,10 +1785,10 @@ class NestedSimulationRead(BaseModel): model_config = ConfigDict( extra='allow', ) - id: UUID = Field(..., title='Id') - type: EntityType | None = None name: str = Field(..., title='Name') description: str = Field(..., title='Description') + id: UUID = Field(..., title='Id') + type: EntityType | None = None simulation_campaign_id: UUID = Field(..., title='Simulation Campaign Id') entity_id: UUID = Field(..., title='Entity Id') scan_parameters: dict[str, Any] = Field(..., title='Scan Parameters') @@ -1797,10 +1798,10 @@ class NestedSkeletonizationConfigRead(BaseModel): model_config = ConfigDict( extra='allow', ) - id: UUID = Field(..., title='Id') - type: EntityType | None = None name: str = Field(..., title='Name') description: str = Field(..., title='Description') + id: UUID = Field(..., title='Id') + type: EntityType | None = None skeletonization_campaign_id: UUID = Field(..., title='Skeletonization Campaign Id') em_cell_mesh_id: UUID = Field(..., title='Em Cell Mesh Id') scan_parameters: dict[str, Any] = Field(..., title='Scan Parameters') @@ -1833,11 +1834,11 @@ class NestedSynaptome(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') id: UUID = Field(..., title='Id') creation_date: AwareDatetime = Field(..., title='Creation Date') update_date: AwareDatetime = Field(..., title='Update Date') - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') seed: int = Field(..., title='Seed') @@ -1937,6 +1938,8 @@ class PlaceholderCellMorphologyProtocolCreate(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_public: bool = Field(default=False, title='Authorized Public') type: Literal['cell_morphology_protocol'] = Field( default='cell_morphology_protocol', title='Type' @@ -1948,6 +1951,8 @@ class PlaceholderCellMorphologyProtocolRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') created_by: NestedPersonRead @@ -2051,6 +2056,12 @@ class PythonRuntimeInfo(BaseModel): ) +class RepairPipelineType(RootModel[Literal['raw', 'curated', 'unraveled', 'repaired']]): + root: Literal['raw', 'curated', 'unraveled', 'repaired'] = Field( + ..., title='RepairPipelineType' + ) + + class RoleAdminUpdate(BaseModel): model_config = ConfigDict( extra='allow', @@ -2136,9 +2147,9 @@ class SimulationCampaignCreate(BaseModel): model_config = ConfigDict( extra='allow', ) - authorized_public: bool = Field(default=False, title='Authorized Public') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + authorized_public: bool = Field(default=False, title='Authorized Public') scan_parameters: dict[str, Any] = Field(..., title='Scan Parameters') entity_id: UUID = Field(..., title='Entity Id') @@ -2159,9 +2170,9 @@ class SimulationCreate(BaseModel): model_config = ConfigDict( extra='allow', ) - authorized_public: bool = Field(default=False, title='Authorized Public') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + authorized_public: bool = Field(default=False, title='Authorized Public') simulation_campaign_id: UUID = Field(..., title='Simulation Campaign Id') entity_id: UUID = Field(..., title='Entity Id') scan_parameters: dict[str, Any] = Field(..., title='Scan Parameters') @@ -2179,6 +2190,8 @@ class SimulationExecutionUserUpdate(BaseModel): model_config = ConfigDict( extra='allow', ) + executor: ExecutorType | None = None + execution_id: UUID | None = Field(default=None, title='Execution Id') start_time: AwareDatetime | NotSet | None = Field( default='', title='Start Time' ) @@ -2231,9 +2244,9 @@ class SimulationResultCreate(BaseModel): model_config = ConfigDict( extra='allow', ) - authorized_public: bool = Field(default=False, title='Authorized Public') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + authorized_public: bool = Field(default=False, title='Authorized Public') simulation_id: UUID = Field(..., title='Simulation Id') @@ -2271,11 +2284,11 @@ class SingleNeuronSimulationUserUpdate(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str | None = Field(default='', title='Name') + description: str | None = Field(default='', title='Description') brain_region_id: UUID | str | None = Field( default='', title='Brain Region Id' ) - name: str | None = Field(default='', title='Name') - description: str | None = Field(default='', title='Description') seed: int | str | None = Field(default='', title='Seed') status: SingleNeuronSimulationStatus | str | None = Field( default='', title='Status' @@ -2293,9 +2306,9 @@ class SingleNeuronSynaptomeCreate(BaseModel): model_config = ConfigDict( extra='allow', ) - authorized_public: bool = Field(default=False, title='Authorized Public') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + authorized_public: bool = Field(default=False, title='Authorized Public') seed: int = Field(..., title='Seed') me_model_id: UUID = Field(..., title='Me Model Id') brain_region_id: UUID = Field(..., title='Brain Region Id') @@ -2305,10 +2318,10 @@ class SingleNeuronSynaptomeSimulationCreate(BaseModel): model_config = ConfigDict( extra='allow', ) - brain_region_id: UUID = Field(..., title='Brain Region Id') - authorized_public: bool = Field(default=False, title='Authorized Public') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + brain_region_id: UUID = Field(..., title='Brain Region Id') + authorized_public: bool = Field(default=False, title='Authorized Public') seed: int = Field(..., title='Seed') status: SingleNeuronSimulationStatus injection_location: list[str] = Field(..., title='Injection Location') @@ -2320,11 +2333,11 @@ class SingleNeuronSynaptomeSimulationUserUpdate(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str | None = Field(default='', title='Name') + description: str | None = Field(default='', title='Description') brain_region_id: UUID | str | None = Field( default='', title='Brain Region Id' ) - name: str | None = Field(default='', title='Name') - description: str | None = Field(default='', title='Description') seed: int | str | None = Field(default='', title='Seed') status: SingleNeuronSimulationStatus | str | None = Field( default='', title='Status' @@ -2361,9 +2374,9 @@ class SkeletonizationConfigCreate(BaseModel): model_config = ConfigDict( extra='allow', ) - authorized_public: bool = Field(default=False, title='Authorized Public') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + authorized_public: bool = Field(default=False, title='Authorized Public') skeletonization_campaign_id: UUID = Field(..., title='Skeletonization Campaign Id') em_cell_mesh_id: UUID = Field(..., title='Em Cell Mesh Id') scan_parameters: dict[str, Any] = Field(..., title='Scan Parameters') @@ -2407,6 +2420,8 @@ class SkeletonizationExecutionUserUpdate(BaseModel): model_config = ConfigDict( extra='allow', ) + executor: ExecutorType | None = None + execution_id: UUID | None = Field(default=None, title='Execution Id') start_time: AwareDatetime | NotSet | None = Field( default='', title='Start Time' ) @@ -2561,6 +2576,8 @@ class SubjectRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') id: UUID = Field(..., title='Id') authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') @@ -2568,8 +2585,6 @@ class SubjectRead(BaseModel): updated_by: NestedPersonRead creation_date: AwareDatetime = Field(..., title='Creation Date') update_date: AwareDatetime = Field(..., title='Update Date') - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') sex: Sex = Field(..., description='Sex of the subject') weight: Weight | None = Field( default=None, description='Weight in grams', title='Weight' @@ -2787,6 +2802,8 @@ class ReadManyAnalysisNotebookEnvironmentGetParametersQuery(BaseModel): class ReadManyAnalysisNotebookExecutionGetParametersQuery(BaseModel): page: int = Field(default=1, ge=1, title='Page') page_size: int = Field(default=100, ge=1, title='Page Size') + executor: ExecutorType | None = Field(default=None, title='Executor') + execution_id: UUID | None = Field(default=None, title='Execution Id') creation_date__lte: AwareDatetime | None = Field( default=None, title='Creation Date Lte' ) @@ -2912,6 +2929,11 @@ class ReadManyAnalysisNotebookResultGetParametersQuery(BaseModel): id: UUID | None = Field(default=None, title='Id') id__in: list[UUID] | None = Field(default=None, title='Id In') order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) contribution__pref_label: str | None = Field( default=None, title='Contribution Pref Label' ) @@ -3002,6 +3024,11 @@ class ReadManyBrainAtlasGetParametersQuery(BaseModel): id: UUID | None = Field(default=None, title='Id') id__in: list[UUID] | None = Field(default=None, title='Id In') order_by: list[str] = Field(default=['name'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) species__name: str | None = Field(default=None, title='Species Name') species__name__in: list[str] | None = Field(default=None, title='Species Name In') species__name__ilike: str | None = Field(default=None, title='Species Name Ilike') @@ -3176,6 +3203,11 @@ class ReadManyCellCompositionGetParametersQuery(BaseModel): id: UUID | None = Field(default=None, title='Id') id__in: list[UUID] | None = Field(default=None, title='Id In') order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) contribution__pref_label: str | None = Field( default=None, title='Contribution Pref Label' ) @@ -3275,6 +3307,11 @@ class ReadManyCellMorphologyGetParametersQuery(BaseModel): id: UUID | None = Field(default=None, title='Id') id__in: list[UUID] | None = Field(default=None, title='Id In') order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) measurement_annotation__creation_date__lte: AwareDatetime | None = Field( default=None, title='Measurement Annotation Creation Date Lte' ) @@ -3447,6 +3484,15 @@ class ReadManyCellMorphologyGetParametersQuery(BaseModel): updated_by__sub_id__in: list[UUID] | None = Field( default=None, title='Updated By Sub Id In' ) + cell_morphology_protocol__name: str | None = Field( + default=None, title='Cell Morphology Protocol Name' + ) + cell_morphology_protocol__name__in: list[str] | None = Field( + default=None, title='Cell Morphology Protocol Name In' + ) + cell_morphology_protocol__name__ilike: str | None = Field( + default=None, title='Cell Morphology Protocol Name Ilike' + ) cell_morphology_protocol__id: UUID | None = Field( default=None, title='Cell Morphology Protocol Id' ) @@ -3486,6 +3532,9 @@ class ReadOneCellMorphologyIdGetParametersQuery(BaseModel): class ReadManyCellMorphologyProtocolGetParametersQuery(BaseModel): page: int = Field(default=1, ge=1, title='Page') page_size: int = Field(default=100, ge=1, title='Page Size') + name: str | None = Field(default=None, title='Name') + name__in: list[str] | None = Field(default=None, title='Name In') + name__ilike: str | None = Field(default=None, title='Name Ilike') creation_date__lte: AwareDatetime | None = Field( default=None, title='Creation Date Lte' ) @@ -3511,6 +3560,11 @@ class ReadManyCellMorphologyProtocolGetParametersQuery(BaseModel): default=None, title='Generation Type In' ) order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) contribution__pref_label: str | None = Field( default=None, title='Contribution Pref Label' ) @@ -3650,6 +3704,11 @@ class ReadManyCircuitGetParametersQuery(BaseModel): number_connections__gte: int | None = Field( default=None, title='Number Connections Gte' ) + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) contribution__pref_label: str | None = Field( default=None, title='Contribution Pref Label' ) @@ -3821,6 +3880,11 @@ class ReadManyCircuitExtractionCampaignGetParametersQuery(BaseModel): id: UUID | None = Field(default=None, title='Id') id__in: list[UUID] | None = Field(default=None, title='Id In') order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) contribution__pref_label: str | None = Field( default=None, title='Contribution Pref Label' ) @@ -3941,6 +4005,11 @@ class ReadManyCircuitExtractionConfigGetParametersQuery(BaseModel): default=None, title='Circuit Extraction Campaign Id In' ) order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) contribution__pref_label: str | None = Field( default=None, title='Contribution Pref Label' ) @@ -4051,6 +4120,8 @@ class ReadManyCircuitExtractionConfigGetParametersQuery(BaseModel): class ReadManyCircuitExtractionExecutionGetParametersQuery(BaseModel): page: int = Field(default=1, ge=1, title='Page') page_size: int = Field(default=100, ge=1, title='Page Size') + executor: ExecutorType | None = Field(default=None, title='Executor') + execution_id: UUID | None = Field(default=None, title='Execution Id') creation_date__lte: AwareDatetime | None = Field( default=None, title='Creation Date Lte' ) @@ -4369,6 +4440,11 @@ class ReadManyElectricalCellRecordingGetParametersQuery(BaseModel): recording_origin__in: list[ElectricalRecordingOrigin] | None = Field( default=None, title='Recording Origin In' ) + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) etype__pref_label: str | None = Field(default=None, title='Etype Pref Label') etype__pref_label__in: list[str] | None = Field( default=None, title='Etype Pref Label In' @@ -4555,6 +4631,11 @@ class ReadManyElectricalRecordingStimulusGetParametersQuery(BaseModel): ) recording_id: UUID | None = Field(default=None, title='Recording Id') recording_id__in: list[UUID] | None = Field(default=None, title='Recording Id In') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) contribution__pref_label: str | None = Field( default=None, title='Contribution Pref Label' ) @@ -4901,6 +4982,11 @@ class ReadManyEmDenseReconstructionDatasetGetParametersQuery(BaseModel): ) contact_email: str | None = Field(default=None, title='Contact Email') order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) contribution__pref_label: str | None = Field( default=None, title='Contribution Pref Label' ) @@ -5074,6 +5160,11 @@ class ReadManyEmodelGetParametersQuery(BaseModel): score__lte: float | None = Field(default=None, title='Score Lte') score__gte: float | None = Field(default=None, title='Score Gte') order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) species__name: str | None = Field(default=None, title='Species Name') species__name__in: list[str] | None = Field(default=None, title='Species Name In') species__name__ilike: str | None = Field(default=None, title='Species Name Ilike') @@ -5452,6 +5543,11 @@ class ReadManyExperimentalBoutonDensityGetParametersQuery(BaseModel): id: UUID | None = Field(default=None, title='Id') id__in: list[UUID] | None = Field(default=None, title='Id In') order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) mtype__pref_label: str | None = Field(default=None, title='Mtype Pref Label') mtype__pref_label__in: list[str] | None = Field( default=None, title='Mtype Pref Label In' @@ -5632,6 +5728,11 @@ class ReadManyExperimentalNeuronDensityGetParametersQuery(BaseModel): id: UUID | None = Field(default=None, title='Id') id__in: list[UUID] | None = Field(default=None, title='Id In') order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) etype__pref_label: str | None = Field(default=None, title='Etype Pref Label') etype__pref_label__in: list[str] | None = Field( default=None, title='Etype Pref Label In' @@ -5821,6 +5922,11 @@ class ReadManyExperimentalSynapsesPerConnectionGetParametersQuery(BaseModel): id: UUID | None = Field(default=None, title='Id') id__in: list[UUID] | None = Field(default=None, title='Id In') order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) subject__name: str | None = Field(default=None, title='Subject Name') subject__name__in: list[str] | None = Field(default=None, title='Subject Name In') subject__name__ilike: str | None = Field(default=None, title='Subject Name Ilike') @@ -6058,6 +6164,11 @@ class ReadManyExternalUrlGetParametersQuery(BaseModel): default=None, title='Update Date Gte' ) order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) created_by__pref_label: str | None = Field( default=None, title='Created By Pref Label' ) @@ -6155,6 +6266,11 @@ class ReadManyIonChannelGetParametersQuery(BaseModel): label: str | None = Field(default=None, title='Label') gene: str | None = Field(default=None, title='Gene') order_by: list[str] = Field(default=['label'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) created_by__pref_label: str | None = Field( default=None, title='Created By Pref Label' ) @@ -6255,6 +6371,7 @@ class ReadManyIonChannelModelGetParametersQuery(BaseModel): is_temperature_dependent: bool | None = Field( default=None, title='Is Temperature Dependent' ) + temperature_celsius: int | None = Field(default=None, title='Temperature Celsius') temperature_celsius__lte: int | None = Field( default=None, title='Temperature Celsius Lte' ) @@ -6262,6 +6379,11 @@ class ReadManyIonChannelModelGetParametersQuery(BaseModel): default=None, title='Temperature Celsius Gte' ) is_stochastic: bool | None = Field(default=None, title='Is Stochastic') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) contribution__pref_label: str | None = Field( default=None, title='Contribution Pref Label' ) @@ -6432,6 +6554,11 @@ class ReadManyIonChannelModelingCampaignGetParametersQuery(BaseModel): id: UUID | None = Field(default=None, title='Id') id__in: list[UUID] | None = Field(default=None, title='Id In') order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) contribution__pref_label: str | None = Field( default=None, title='Contribution Pref Label' ) @@ -6565,6 +6692,11 @@ class ReadManyIonChannelModelingConfigGetParametersQuery(BaseModel): default=None, title='Ion Channel Modeling Campaign Id In' ) order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) contribution__pref_label: str | None = Field( default=None, title='Contribution Pref Label' ) @@ -6660,6 +6792,8 @@ class ReadManyIonChannelModelingConfigGetParametersQuery(BaseModel): class ReadManyIonChannelModelingExecutionGetParametersQuery(BaseModel): page: int = Field(default=1, ge=1, title='Page') page_size: int = Field(default=100, ge=1, title='Page Size') + executor: ExecutorType | None = Field(default=None, title='Executor') + execution_id: UUID | None = Field(default=None, title='Execution Id') creation_date__lte: AwareDatetime | None = Field( default=None, title='Creation Date Lte' ) @@ -6811,6 +6945,11 @@ class ReadManyIonChannelRecordingGetParametersQuery(BaseModel): temperature__gte: float | None = Field(default=None, title='Temperature Gte') cell_line: str | None = Field(default=None, title='Cell Line') cell_line__ilike: str | None = Field(default=None, title='Cell Line Ilike') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) contribution__pref_label: str | None = Field( default=None, title='Contribution Pref Label' ) @@ -6981,6 +7120,11 @@ class ReadManyLicenseGetParametersQuery(BaseModel): label: str | None = Field(default=None, title='Label') label__ilike: str | None = Field(default=None, title='Label Ilike') order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) class ReadManyMeasurementAnnotationGetParametersQuery(BaseModel): @@ -7063,6 +7207,11 @@ class ReadManyMemodelGetParametersQuery(BaseModel): id: UUID | None = Field(default=None, title='Id') id__in: list[UUID] | None = Field(default=None, title='Id In') order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) brain_region__name: str | None = Field(default=None, title='Brain Region Name') brain_region__name__in: list[str] | None = Field( default=None, title='Brain Region Name In' @@ -8002,6 +8151,11 @@ class ReadManySkeletonizationCampaignGetParametersQuery(BaseModel): id: UUID | None = Field(default=None, title='Id') id__in: list[UUID] | None = Field(default=None, title='Id In') order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) contribution__pref_label: str | None = Field( default=None, title='Contribution Pref Label' ) @@ -8139,6 +8293,11 @@ class ReadManySkeletonizationConfigGetParametersQuery(BaseModel): default=None, title='Em Cell Mesh Id In' ) order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) contribution__pref_label: str | None = Field( default=None, title='Contribution Pref Label' ) @@ -8234,6 +8393,8 @@ class ReadManySkeletonizationConfigGetParametersQuery(BaseModel): class ReadManySkeletonizationExecutionGetParametersQuery(BaseModel): page: int = Field(default=1, ge=1, title='Page') page_size: int = Field(default=100, ge=1, title='Page Size') + executor: ExecutorType | None = Field(default=None, title='Executor') + execution_id: UUID | None = Field(default=None, title='Execution Id') creation_date__lte: AwareDatetime | None = Field( default=None, title='Creation Date Lte' ) @@ -8367,6 +8528,11 @@ class ReadManySimulationGetParametersQuery(BaseModel): default=None, title='Simulation Campaign Id In' ) order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) contribution__pref_label: str | None = Field( default=None, title='Contribution Pref Label' ) @@ -8496,6 +8662,11 @@ class ReadManySimulationCampaignGetParametersQuery(BaseModel): entity_id: UUID | None = Field(default=None, title='Entity Id') entity_id__in: list[UUID] | None = Field(default=None, title='Entity Id In') order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) contribution__pref_label: str | None = Field( default=None, title='Contribution Pref Label' ) @@ -8623,6 +8794,8 @@ class ReadManySimulationCampaignGetParametersQuery(BaseModel): class ReadManySimulationExecutionGetParametersQuery(BaseModel): page: int = Field(default=1, ge=1, title='Page') page_size: int = Field(default=100, ge=1, title='Page Size') + executor: ExecutorType | None = Field(default=None, title='Executor') + execution_id: UUID | None = Field(default=None, title='Execution Id') creation_date__lte: AwareDatetime | None = Field( default=None, title='Creation Date Lte' ) @@ -8757,6 +8930,11 @@ class ReadManySingleNeuronSimulationGetParametersQuery(BaseModel): id__in: list[UUID] | None = Field(default=None, title='Id In') status: SingleNeuronSimulationStatus | None = Field(default=None, title='Status') order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) brain_region__name: str | None = Field(default=None, title='Brain Region Name') brain_region__name__in: list[str] | None = Field( default=None, title='Brain Region Name In' @@ -9179,6 +9357,11 @@ class ReadManySingleNeuronSynaptomeGetParametersQuery(BaseModel): id: UUID | None = Field(default=None, title='Id') id__in: list[UUID] | None = Field(default=None, title='Id In') order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) brain_region__name: str | None = Field(default=None, title='Brain Region Name') brain_region__name__in: list[str] | None = Field( default=None, title='Brain Region Name In' @@ -9602,6 +9785,11 @@ class ReadManySingleNeuronSynaptomeSimulationGetParametersQuery(BaseModel): id__in: list[UUID] | None = Field(default=None, title='Id In') status: SingleNeuronSimulationStatus | None = Field(default=None, title='Status') order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) brain_region__name: str | None = Field(default=None, title='Brain Region Name') brain_region__name__in: list[str] | None = Field( default=None, title='Brain Region Name In' @@ -10155,6 +10343,11 @@ class ReadManySubjectGetParametersQuery(BaseModel): id__in: list[UUID] | None = Field(default=None, title='Id In') age_value: timedelta | None = Field(default=None, title='Age Value') order_by: list[str] = Field(default=['-creation_date'], title='Order By') + ilike_search: str | None = Field( + default=None, + description="Search text with wildcard support. Use * for zero or more characters and ? for exactly one character. All other characters are treated as literals. Examples: 'test*' matches 'testing', 'file?.txt' matches 'file1.txt'. search_model_fields: name, description", + title='Ilike Search', + ) species__name: str | None = Field(default=None, title='Species Name') species__name__in: list[str] | None = Field(default=None, title='Species Name In') species__name__ilike: str | None = Field(default=None, title='Species Name Ilike') @@ -10380,10 +10573,31 @@ class AnalysisNotebookEnvironmentUpdate(BaseModel): ) +class AnalysisNotebookExecutionCreate(BaseModel): + model_config = ConfigDict( + extra='allow', + ) + executor: ExecutorType | None = None + execution_id: UUID | None = Field(default=None, title='Execution Id') + authorized_public: bool = Field(default=False, title='Authorized Public') + start_time: AwareDatetime | None = Field(default=None, title='Start Time') + end_time: AwareDatetime | None = Field(default=None, title='End Time') + used_ids: list[UUID] = Field(default=[], title='Used Ids') + generated_ids: list[UUID] = Field(default=[], title='Generated Ids') + analysis_notebook_template_id: UUID | None = Field( + default=None, title='Analysis Notebook Template Id' + ) + analysis_notebook_environment_id: UUID = Field( + ..., title='Analysis Notebook Environment Id' + ) + + class AnalysisNotebookExecutionUpdate(BaseModel): model_config = ConfigDict( extra='allow', ) + executor: ExecutorType | None = None + execution_id: UUID | None = Field(default=None, title='Execution Id') start_time: AwareDatetime | NotSet | None = Field( default='', title='Start Time' ) @@ -10528,6 +10742,8 @@ class CellMorphologyCreate(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_public: bool = Field(default=False, title='Authorized Public') license_id: UUID | None = Field(default=None, title='License Id') brain_region_id: UUID = Field(..., title='Brain Region Id') @@ -10552,10 +10768,10 @@ class CellMorphologyCreate(BaseModel): description='Text provided by the data creators to inform users about data caveats, limitations, or required attribution practices.', title='Notice Text', ) - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') location: PointLocationBase | None = None legacy_id: list[str] | None = Field(default=None, title='Legacy Id') + has_segmented_spines: bool = Field(default=False, title='Has Segmented Spines') + repair_pipeline_state: RepairPipelineType | None = None cell_morphology_protocol_id: UUID | None = Field( default=None, title='Cell Morphology Protocol Id' ) @@ -10565,6 +10781,8 @@ class CellMorphologyUserUpdate(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str | None = Field(default='', title='Name') + description: str | None = Field(default='', title='Description') license_id: UUID | str | None = Field(default='', title='License Id') brain_region_id: UUID | str | None = Field( default='', title='Brain Region Id' @@ -10576,12 +10794,16 @@ class CellMorphologyUserUpdate(BaseModel): contact_email: str | None = Field(default='', title='Contact Email') published_in: str | None = Field(default='', title='Published In') notice_text: str | None = Field(default='', title='Notice Text') - name: str | None = Field(default='', title='Name') - description: str | None = Field(default='', title='Description') location: PointLocationBase | str | None = Field( default='', title='Location' ) legacy_id: list[str] | str | None = Field(default='', title='Legacy Id') + has_segmented_spines: bool | str | None = Field( + default='', title='Has Segmented Spines' + ) + repair_pipeline_state: RepairPipelineType | str | None = Field( + default='', title='Repair Pipeline State' + ) cell_morphology_protocol_id: UUID | str | None = Field( default='', title='Cell Morphology Protocol Id' ) @@ -10591,6 +10813,8 @@ class CircuitCreate(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_public: bool = Field(default=False, title='Authorized Public') license_id: UUID | None = Field(default=None, title='License Id') brain_region_id: UUID = Field(..., title='Brain Region Id') @@ -10615,8 +10839,6 @@ class CircuitCreate(BaseModel): description='Text provided by the data creators to inform users about data caveats, limitations, or required attribution practices.', title='Notice Text', ) - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') has_morphologies: bool = Field(default=False, title='Has Morphologies') has_point_neurons: bool = Field(default=False, title='Has Point Neurons') has_electrical_cell_models: bool = Field( @@ -10642,6 +10864,8 @@ class CircuitExtractionExecutionCreate(BaseModel): model_config = ConfigDict( extra='allow', ) + executor: ExecutorType | None = None + execution_id: UUID | None = Field(default=None, title='Execution Id') authorized_public: bool = Field(default=False, title='Authorized Public') start_time: AwareDatetime | None = Field(default=None, title='Start Time') end_time: AwareDatetime | None = Field(default=None, title='End Time') @@ -10654,6 +10878,8 @@ class CircuitExtractionExecutionRead(BaseModel): model_config = ConfigDict( extra='allow', ) + executor: ExecutorType | None = None + execution_id: UUID | None = Field(default=None, title='Execution Id') authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') creation_date: AwareDatetime = Field(..., title='Creation Date') @@ -10673,6 +10899,8 @@ class CircuitExtractionExecutionUserUpdate(BaseModel): model_config = ConfigDict( extra='allow', ) + executor: ExecutorType | None = None + execution_id: UUID | None = Field(default=None, title='Execution Id') start_time: AwareDatetime | NotSet | None = Field( default='', title='Start Time' ) @@ -10693,6 +10921,8 @@ class ComputationallySynthesizedCellMorphologyProtocolRead(BaseModel): default=None, title='Protocol Document' ) protocol_design: CellMorphologyProtocolDesign + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') created_by: NestedPersonRead @@ -10752,6 +10982,8 @@ class DigitalReconstructionCellMorphologyProtocolCreate(BaseModel): default=None, title='Protocol Document' ) protocol_design: CellMorphologyProtocolDesign + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_public: bool = Field(default=False, title='Authorized Public') type: Literal['cell_morphology_protocol'] = Field( default='cell_morphology_protocol', title='Type' @@ -10779,6 +11011,8 @@ class DigitalReconstructionCellMorphologyProtocolRead(BaseModel): default=None, title='Protocol Document' ) protocol_design: CellMorphologyProtocolDesign + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') created_by: NestedPersonRead @@ -10849,6 +11083,8 @@ class EMDenseReconstructionDatasetCreate(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_public: bool = Field(default=False, title='Authorized Public') license_id: UUID | None = Field(default=None, title='License Id') brain_region_id: UUID = Field(..., title='Brain Region Id') @@ -10873,8 +11109,6 @@ class EMDenseReconstructionDatasetCreate(BaseModel): description='Text provided by the data creators to inform users about data caveats, limitations, or required attribution practices.', title='Notice Text', ) - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') protocol_document: ProtocolDocument | None = Field( default=None, title='Protocol Document' ) @@ -10919,6 +11153,8 @@ class ElectricalCellRecordingCreate(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_public: bool = Field(default=False, title='Authorized Public') license_id: UUID | None = Field(default=None, title='License Id') brain_region_id: UUID = Field(..., title='Brain Region Id') @@ -10943,8 +11179,6 @@ class ElectricalCellRecordingCreate(BaseModel): description='Text provided by the data creators to inform users about data caveats, limitations, or required attribution practices.', title='Notice Text', ) - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') ljp: float = Field( default=0.0, description='Correction applied to the voltage trace, in mV', @@ -10980,6 +11214,8 @@ class ElectricalCellRecordingUserUpdate(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str | None = Field(default='', title='Name') + description: str | None = Field(default='', title='Description') license_id: UUID | str | None = Field(default='', title='License Id') brain_region_id: UUID | str | None = Field( default='', title='Brain Region Id' @@ -10991,8 +11227,6 @@ class ElectricalCellRecordingUserUpdate(BaseModel): contact_email: str | None = Field(default='', title='Contact Email') published_in: str | None = Field(default='', title='Published In') notice_text: str | None = Field(default='', title='Notice Text') - name: str | None = Field(default='', title='Name') - description: str | None = Field(default='', title='Description') ljp: float | str | None = Field(default='', title='Ljp') recording_location: list[str] | str | None = Field( default='', title='Recording Location' @@ -11012,9 +11246,9 @@ class ElectricalRecordingStimulusCreate(BaseModel): model_config = ConfigDict( extra='allow', ) - authorized_public: bool = Field(default=False, title='Authorized Public') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + authorized_public: bool = Field(default=False, title='Authorized Public') dt: float | None = Field(default=None, title='Dt') injection_type: ElectricalRecordingStimulusType shape: ElectricalRecordingStimulusShape @@ -11027,6 +11261,8 @@ class ElectricalRecordingStimulusRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') created_by: NestedPersonRead @@ -11035,8 +11271,6 @@ class ElectricalRecordingStimulusRead(BaseModel): update_date: AwareDatetime = Field(..., title='Update Date') type: EntityType | None = None id: UUID = Field(..., title='Id') - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') dt: float | None = Field(default=None, title='Dt') injection_type: ElectricalRecordingStimulusType shape: ElectricalRecordingStimulusShape @@ -11061,11 +11295,13 @@ class ExemplarMorphology(BaseModel): model_config = ConfigDict( extra='allow', ) - id: UUID = Field(..., title='Id') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + id: UUID = Field(..., title='Id') location: PointLocationBase | None = None legacy_id: list[str] | None = Field(default=None, title='Legacy Id') + has_segmented_spines: bool = Field(default=False, title='Has Segmented Spines') + repair_pipeline_state: RepairPipelineType | None = None creation_date: AwareDatetime = Field(..., title='Creation Date') update_date: AwareDatetime = Field(..., title='Update Date') @@ -11074,6 +11310,8 @@ class ExternalUrlRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') created_by: NestedPersonRead updated_by: NestedPersonRead creation_date: AwareDatetime = Field(..., title='Creation Date') @@ -11081,8 +11319,6 @@ class ExternalUrlRead(BaseModel): id: UUID = Field(..., title='Id') source: ExternalSource url: AnyUrl = Field(..., title='Url') - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') source_name: str = Field(..., title='Source Name') @@ -11096,6 +11332,8 @@ class IonChannelModelingExecutionCreate(BaseModel): model_config = ConfigDict( extra='allow', ) + executor: ExecutorType | None = None + execution_id: UUID | None = Field(default=None, title='Execution Id') authorized_public: bool = Field(default=False, title='Authorized Public') start_time: AwareDatetime | None = Field(default=None, title='Start Time') end_time: AwareDatetime | None = Field(default=None, title='End Time') @@ -11108,6 +11346,8 @@ class IonChannelModelingExecutionRead(BaseModel): model_config = ConfigDict( extra='allow', ) + executor: ExecutorType | None = None + execution_id: UUID | None = Field(default=None, title='Execution Id') authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') creation_date: AwareDatetime = Field(..., title='Creation Date') @@ -11127,6 +11367,8 @@ class IonChannelModelingExecutionUserUpdate(BaseModel): model_config = ConfigDict( extra='allow', ) + executor: ExecutorType | None = None + execution_id: UUID | None = Field(default=None, title='Execution Id') start_time: AwareDatetime | NotSet | None = Field( default='', title='Start Time' ) @@ -11143,13 +11385,13 @@ class IonChannelRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') created_by: NestedPersonRead updated_by: NestedPersonRead creation_date: AwareDatetime = Field(..., title='Creation Date') update_date: AwareDatetime = Field(..., title='Update Date') id: UUID = Field(..., title='Id') - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') label: str = Field(..., title='Label') gene: str = Field(..., title='Gene') synonyms: list[str] = Field(..., title='Synonyms') @@ -11455,9 +11697,9 @@ class MEModelCreate(BaseModel): model_config = ConfigDict( extra='allow', ) - authorized_public: bool = Field(default=False, title='Authorized Public') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + authorized_public: bool = Field(default=False, title='Authorized Public') validation_status: ValidationStatus = Field( default_factory=lambda: ValidationStatus.model_validate('created') ) @@ -11549,6 +11791,8 @@ class ModifiedReconstructionCellMorphologyProtocolRead(BaseModel): default=None, title='Protocol Document' ) protocol_design: CellMorphologyProtocolDesign + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') created_by: NestedPersonRead @@ -11578,10 +11822,10 @@ class NestedAnalysisNotebookTemplateRead(BaseModel): model_config = ConfigDict( extra='allow', ) - id: UUID = Field(..., title='Id') - type: EntityType | None = None name: str = Field(..., title='Name') description: str = Field(..., title='Description') + id: UUID = Field(..., title='Id') + type: EntityType | None = None specifications: AnalysisNotebookTemplateSpecificationsOutput | None = None scale: AnalysisScale @@ -11603,6 +11847,8 @@ class NestedDigitalReconstructionCellMorphologyProtocolRead(BaseModel): default=None, title='Protocol Document' ) protocol_design: CellMorphologyProtocolDesign + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') id: UUID = Field(..., title='Id') type: Literal['cell_morphology_protocol'] = Field( default='cell_morphology_protocol', title='Type' @@ -11626,11 +11872,11 @@ class NestedMEModel(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') id: UUID = Field(..., title='Id') creation_date: AwareDatetime = Field(..., title='Creation Date') update_date: AwareDatetime = Field(..., title='Update Date') - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') validation_status: ValidationStatus = Field( default_factory=lambda: ValidationStatus.model_validate('created') ) @@ -11642,9 +11888,9 @@ class NestedSubjectRead(BaseModel): model_config = ConfigDict( extra='allow', ) - id: UUID = Field(..., title='Id') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + id: UUID = Field(..., title='Id') sex: Sex = Field(..., description='Sex of the subject') weight: Weight | None = Field( default=None, description='Weight in grams', title='Weight' @@ -11679,6 +11925,8 @@ class SimulationCampaignRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') creation_date: AwareDatetime = Field(..., title='Creation Date') @@ -11688,8 +11936,6 @@ class SimulationCampaignRead(BaseModel): assets: list[AssetRead] = Field(..., title='Assets') id: UUID = Field(..., title='Id') type: EntityType | None = None - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') scan_parameters: dict[str, Any] = Field(..., title='Scan Parameters') entity_id: UUID = Field(..., title='Entity Id') simulations: list[NestedSimulationRead] = Field(..., title='Simulations') @@ -11699,6 +11945,8 @@ class SimulationExecutionCreate(BaseModel): model_config = ConfigDict( extra='allow', ) + executor: ExecutorType | None = None + execution_id: UUID | None = Field(default=None, title='Execution Id') authorized_public: bool = Field(default=False, title='Authorized Public') start_time: AwareDatetime | None = Field(default=None, title='Start Time') end_time: AwareDatetime | None = Field(default=None, title='End Time') @@ -11711,6 +11959,8 @@ class SimulationExecutionRead(BaseModel): model_config = ConfigDict( extra='allow', ) + executor: ExecutorType | None = None + execution_id: UUID | None = Field(default=None, title='Execution Id') authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') creation_date: AwareDatetime = Field(..., title='Creation Date') @@ -11730,6 +11980,8 @@ class SimulationRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') creation_date: AwareDatetime = Field(..., title='Creation Date') @@ -11739,8 +11991,6 @@ class SimulationRead(BaseModel): assets: list[AssetRead] = Field(..., title='Assets') id: UUID = Field(..., title='Id') type: EntityType | None = None - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') simulation_campaign_id: UUID = Field(..., title='Simulation Campaign Id') entity_id: UUID = Field(..., title='Entity Id') scan_parameters: dict[str, Any] = Field(..., title='Scan Parameters') @@ -11750,6 +12000,8 @@ class SimulationResultRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') creation_date: AwareDatetime = Field(..., title='Creation Date') @@ -11759,8 +12011,6 @@ class SimulationResultRead(BaseModel): assets: list[AssetRead] = Field(..., title='Assets') id: UUID = Field(..., title='Id') type: EntityType | None = None - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') simulation_id: UUID = Field(..., title='Simulation Id') @@ -11768,10 +12018,10 @@ class SingleNeuronSimulationCreate(BaseModel): model_config = ConfigDict( extra='allow', ) - brain_region_id: UUID = Field(..., title='Brain Region Id') - authorized_public: bool = Field(default=False, title='Authorized Public') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + brain_region_id: UUID = Field(..., title='Brain Region Id') + authorized_public: bool = Field(default=False, title='Authorized Public') seed: int = Field(..., title='Seed') status: SingleNeuronSimulationStatus injection_location: list[str] = Field(..., title='Injection Location') @@ -11783,6 +12033,8 @@ class SingleNeuronSimulationRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') created_by: NestedPersonRead updated_by: NestedPersonRead assets: list[AssetRead] = Field(..., title='Assets') @@ -11793,8 +12045,6 @@ class SingleNeuronSimulationRead(BaseModel): authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') brain_region: BrainRegionRead - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') seed: int = Field(..., title='Seed') status: SingleNeuronSimulationStatus injection_location: list[str] = Field(..., title='Injection Location') @@ -11806,6 +12056,8 @@ class SingleNeuronSynaptomeRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') created_by: NestedPersonRead updated_by: NestedPersonRead assets: list[AssetRead] = Field(..., title='Assets') @@ -11818,8 +12070,6 @@ class SingleNeuronSynaptomeRead(BaseModel): id: UUID = Field(..., title='Id') authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') seed: int = Field(..., title='Seed') me_model: NestedMEModel brain_region: BrainRegionRead @@ -11829,6 +12079,8 @@ class SingleNeuronSynaptomeSimulationRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') created_by: NestedPersonRead updated_by: NestedPersonRead assets: list[AssetRead] = Field(..., title='Assets') @@ -11839,8 +12091,6 @@ class SingleNeuronSynaptomeSimulationRead(BaseModel): authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') brain_region: BrainRegionRead - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') seed: int = Field(..., title='Seed') status: SingleNeuronSimulationStatus injection_location: list[str] = Field(..., title='Injection Location') @@ -11852,6 +12102,8 @@ class SkeletonizationCampaignRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -11864,8 +12116,6 @@ class SkeletonizationCampaignRead(BaseModel): assets: list[AssetRead] = Field(..., title='Assets') id: UUID = Field(..., title='Id') type: EntityType | None = None - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') scan_parameters: dict[str, Any] = Field(..., title='Scan Parameters') input_meshes: list[NestedEMCellMeshRead] = Field(..., title='Input Meshes') skeletonization_configs: list[NestedSkeletonizationConfigRead] = Field( @@ -11877,6 +12127,8 @@ class SkeletonizationConfigRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -11889,8 +12141,6 @@ class SkeletonizationConfigRead(BaseModel): assets: list[AssetRead] = Field(..., title='Assets') id: UUID = Field(..., title='Id') type: EntityType | None = None - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') skeletonization_campaign_id: UUID = Field(..., title='Skeletonization Campaign Id') em_cell_mesh_id: UUID = Field(..., title='Em Cell Mesh Id') scan_parameters: dict[str, Any] = Field(..., title='Scan Parameters') @@ -11900,6 +12150,8 @@ class SkeletonizationExecutionCreate(BaseModel): model_config = ConfigDict( extra='allow', ) + executor: ExecutorType | None = None + execution_id: UUID | None = Field(default=None, title='Execution Id') authorized_public: bool = Field(default=False, title='Authorized Public') start_time: AwareDatetime | None = Field(default=None, title='Start Time') end_time: AwareDatetime | None = Field(default=None, title='End Time') @@ -11912,6 +12164,8 @@ class SkeletonizationExecutionRead(BaseModel): model_config = ConfigDict( extra='allow', ) + executor: ExecutorType | None = None + execution_id: UUID | None = Field(default=None, title='Execution Id') authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') creation_date: AwareDatetime = Field(..., title='Creation Date') @@ -11964,6 +12218,8 @@ class AnalysisNotebookExecutionRead(BaseModel): model_config = ConfigDict( extra='allow', ) + executor: ExecutorType | None = None + execution_id: UUID | None = Field(default=None, title='Execution Id') authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') creation_date: AwareDatetime = Field(..., title='Creation Date') @@ -11984,6 +12240,8 @@ class AnalysisNotebookResultRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_project_id: UUID4 = Field(..., title='Authorized Project Id') authorized_public: bool = Field(default=False, title='Authorized Public') creation_date: AwareDatetime = Field(..., title='Creation Date') @@ -11993,17 +12251,15 @@ class AnalysisNotebookResultRead(BaseModel): assets: list[AssetRead] = Field(..., title='Assets') id: UUID = Field(..., title='Id') type: EntityType | None = None - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') class AnalysisNotebookTemplateCreate(BaseModel): model_config = ConfigDict( extra='allow', ) - authorized_public: bool = Field(default=False, title='Authorized Public') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + authorized_public: bool = Field(default=False, title='Authorized Public') specifications: AnalysisNotebookTemplateSpecificationsInput | None = None scale: AnalysisScale @@ -12012,6 +12268,8 @@ class AnalysisNotebookTemplateRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -12024,8 +12282,6 @@ class AnalysisNotebookTemplateRead(BaseModel): assets: list[AssetRead] = Field(..., title='Assets') id: UUID = Field(..., title='Id') type: EntityType | None = None - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') specifications: AnalysisNotebookTemplateSpecificationsOutput | None = None scale: AnalysisScale @@ -12042,6 +12298,8 @@ class CellCompositionRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') assets: list[AssetRead] = Field(..., title='Assets') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' @@ -12052,8 +12310,6 @@ class CellCompositionRead(BaseModel): authorized_public: bool = Field(default=False, title='Authorized Public') creation_date: AwareDatetime = Field(..., title='Creation Date') update_date: AwareDatetime = Field(..., title='Update Date') - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') class CellMorphologyProtocolCreate( @@ -12092,6 +12348,8 @@ class CircuitExtractionCampaignRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -12104,8 +12362,6 @@ class CircuitExtractionCampaignRead(BaseModel): assets: list[AssetRead] = Field(..., title='Assets') id: UUID = Field(..., title='Id') type: EntityType | None = None - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') scan_parameters: dict[str, Any] = Field(..., title='Scan Parameters') @@ -12113,6 +12369,8 @@ class CircuitExtractionConfigRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -12125,8 +12383,6 @@ class CircuitExtractionConfigRead(BaseModel): assets: list[AssetRead] = Field(..., title='Assets') id: UUID = Field(..., title='Id') type: EntityType | None = None - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') circuit_id: UUID = Field(..., title='Circuit Id') scan_parameters: dict[str, Any] = Field(..., title='Scan Parameters') @@ -12135,6 +12391,8 @@ class CircuitRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -12170,8 +12428,6 @@ class CircuitRead(BaseModel): description='Text provided by the data creators to inform users about data caveats, limitations, or required attribution practices.', title='Notice Text', ) - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') has_morphologies: bool = Field(default=False, title='Has Morphologies') has_point_neurons: bool = Field(default=False, title='Has Point Neurons') has_electrical_cell_models: bool = Field( @@ -12241,6 +12497,8 @@ class EMDenseReconstructionDatasetRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -12276,8 +12534,6 @@ class EMDenseReconstructionDatasetRead(BaseModel): description='Text provided by the data creators to inform users about data caveats, limitations, or required attribution practices.', title='Notice Text', ) - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') protocol_document: ProtocolDocument | None = Field( default=None, title='Protocol Document' ) @@ -12307,6 +12563,8 @@ class EModelRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -12318,8 +12576,6 @@ class EModelRead(BaseModel): authorized_public: bool = Field(default=False, title='Authorized Public') creation_date: AwareDatetime = Field(..., title='Creation Date') update_date: AwareDatetime = Field(..., title='Update Date') - description: str = Field(..., title='Description') - name: str = Field(..., title='Name') iteration: str = Field(..., title='Iteration') score: float = Field(..., title='Score') seed: int = Field(..., title='Seed') @@ -12336,6 +12592,8 @@ class ElectricalCellRecordingRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -12371,8 +12629,6 @@ class ElectricalCellRecordingRead(BaseModel): description='Text provided by the data creators to inform users about data caveats, limitations, or required attribution practices.', title='Notice Text', ) - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') ljp: float = Field( default=0.0, description='Correction applied to the voltage trace, in mV', @@ -12414,10 +12670,10 @@ class ExperimentalBoutonDensityCreate(BaseModel): model_config = ConfigDict( extra='allow', ) - authorized_public: bool = Field(default=False, title='Authorized Public') - license_id: UUID | None = Field(default=None, title='License Id') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + authorized_public: bool = Field(default=False, title='Authorized Public') + license_id: UUID | None = Field(default=None, title='License Id') subject_id: UUID = Field(..., title='Subject Id') brain_region_id: UUID = Field(..., title='Brain Region Id') legacy_id: str | None = Field(..., title='Legacy Id') @@ -12429,6 +12685,8 @@ class ExperimentalBoutonDensityRead(BaseModel): extra='allow', ) subject: NestedSubjectRead + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -12441,8 +12699,6 @@ class ExperimentalBoutonDensityRead(BaseModel): id: UUID = Field(..., title='Id') creation_date: AwareDatetime = Field(..., title='Creation Date') update_date: AwareDatetime = Field(..., title='Update Date') - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') measurements: list[MeasurementRecordRead] = Field(..., title='Measurements') assets: list[AssetRead] = Field(..., title='Assets') brain_region: BrainRegionRead @@ -12453,9 +12709,9 @@ class ExperimentalBoutonDensityUserUpdate(BaseModel): model_config = ConfigDict( extra='allow', ) - license_id: UUID | str | None = Field(default='', title='License Id') name: str | None = Field(default='', title='Name') description: str | None = Field(default='', title='Description') + license_id: UUID | str | None = Field(default='', title='License Id') subject_id: UUID | str | None = Field(default='', title='Subject Id') brain_region_id: UUID | str | None = Field( default='', title='Brain Region Id' @@ -12474,6 +12730,8 @@ class ExperimentalNeuronDensityRead(BaseModel): extra='allow', ) subject: NestedSubjectRead + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -12486,8 +12744,6 @@ class ExperimentalNeuronDensityRead(BaseModel): id: UUID = Field(..., title='Id') creation_date: AwareDatetime = Field(..., title='Creation Date') update_date: AwareDatetime = Field(..., title='Update Date') - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') measurements: list[MeasurementRecordRead] = Field(..., title='Measurements') assets: list[AssetRead] = Field(..., title='Assets') brain_region: BrainRegionRead @@ -12502,10 +12758,10 @@ class ExperimentalSynapsesPerConnectionCreate(BaseModel): model_config = ConfigDict( extra='allow', ) - authorized_public: bool = Field(default=False, title='Authorized Public') - license_id: UUID | None = Field(default=None, title='License Id') name: str = Field(..., title='Name') description: str = Field(..., title='Description') + authorized_public: bool = Field(default=False, title='Authorized Public') + license_id: UUID | None = Field(default=None, title='License Id') subject_id: UUID = Field(..., title='Subject Id') brain_region_id: UUID = Field(..., title='Brain Region Id') legacy_id: str | None = Field(..., title='Legacy Id') @@ -12521,6 +12777,8 @@ class ExperimentalSynapsesPerConnectionRead(BaseModel): extra='allow', ) subject: NestedSubjectRead + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -12533,8 +12791,6 @@ class ExperimentalSynapsesPerConnectionRead(BaseModel): id: UUID = Field(..., title='Id') creation_date: AwareDatetime = Field(..., title='Creation Date') update_date: AwareDatetime = Field(..., title='Update Date') - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') measurements: list[MeasurementRecordRead] = Field(..., title='Measurements') assets: list[AssetRead] = Field(..., title='Assets') brain_region: BrainRegionRead @@ -12548,9 +12804,9 @@ class ExperimentalSynapsesPerConnectionUserUpdate(BaseModel): model_config = ConfigDict( extra='allow', ) - license_id: UUID | str | None = Field(default='', title='License Id') name: str | None = Field(default='', title='Name') description: str | None = Field(default='', title='Description') + license_id: UUID | str | None = Field(default='', title='License Id') subject_id: UUID | str | None = Field(default='', title='Subject Id') brain_region_id: UUID | str | None = Field( default='', title='Brain Region Id' @@ -12571,6 +12827,8 @@ class IonChannelModelCreate(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') authorized_public: bool = Field(default=False, title='Authorized Public') license_id: UUID | None = Field(default=None, title='License Id') brain_region_id: UUID = Field(..., title='Brain Region Id') @@ -12595,8 +12853,6 @@ class IonChannelModelCreate(BaseModel): description='Text provided by the data creators to inform users about data caveats, limitations, or required attribution practices.', title='Notice Text', ) - description: str = Field(..., title='Description') - name: str = Field(..., title='Name') nmodl_suffix: str = Field(..., title='Nmodl Suffix') is_ljp_corrected: bool = Field(default=False, title='Is Ljp Corrected') is_temperature_dependent: bool = Field( @@ -12611,6 +12867,8 @@ class IonChannelModelExpanded(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -12646,8 +12904,6 @@ class IonChannelModelExpanded(BaseModel): description='Text provided by the data creators to inform users about data caveats, limitations, or required attribution practices.', title='Notice Text', ) - description: str = Field(..., title='Description') - name: str = Field(..., title='Name') nmodl_suffix: str = Field(..., title='Nmodl Suffix') is_ljp_corrected: bool = Field(default=False, title='Is Ljp Corrected') is_temperature_dependent: bool = Field( @@ -12662,6 +12918,8 @@ class IonChannelModelRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') creation_date: AwareDatetime = Field(..., title='Creation Date') update_date: AwareDatetime = Field(..., title='Update Date') brain_region: BrainRegionRead @@ -12690,8 +12948,6 @@ class IonChannelModelRead(BaseModel): description='Text provided by the data creators to inform users about data caveats, limitations, or required attribution practices.', title='Notice Text', ) - description: str = Field(..., title='Description') - name: str = Field(..., title='Name') nmodl_suffix: str = Field(..., title='Nmodl Suffix') is_ljp_corrected: bool = Field(default=False, title='Is Ljp Corrected') is_temperature_dependent: bool = Field( @@ -12706,6 +12962,8 @@ class IonChannelModelUserUpdate(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str | None = Field(default='', title='Name') + description: str | None = Field(default='', title='Description') license_id: UUID | str | None = Field(default='', title='License Id') brain_region_id: UUID | str | None = Field( default='', title='Brain Region Id' @@ -12717,8 +12975,6 @@ class IonChannelModelUserUpdate(BaseModel): contact_email: str | None = Field(default='', title='Contact Email') published_in: str | None = Field(default='', title='Published In') notice_text: str | None = Field(default='', title='Notice Text') - description: str | None = Field(default='', title='Description') - name: str | None = Field(default='', title='Name') nmodl_suffix: str | None = Field(default='', title='Nmodl Suffix') is_ljp_corrected: bool | str | None = Field( default='', title='Is Ljp Corrected' @@ -12739,6 +12995,8 @@ class IonChannelModelWAssets(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') assets: list[AssetRead] = Field(..., title='Assets') creation_date: AwareDatetime = Field(..., title='Creation Date') update_date: AwareDatetime = Field(..., title='Update Date') @@ -12768,8 +13026,6 @@ class IonChannelModelWAssets(BaseModel): description='Text provided by the data creators to inform users about data caveats, limitations, or required attribution practices.', title='Notice Text', ) - description: str = Field(..., title='Description') - name: str = Field(..., title='Name') nmodl_suffix: str = Field(..., title='Nmodl Suffix') is_ljp_corrected: bool = Field(default=False, title='Is Ljp Corrected') is_temperature_dependent: bool = Field( @@ -12784,6 +13040,8 @@ class IonChannelModelingCampaignRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -12796,8 +13054,6 @@ class IonChannelModelingCampaignRead(BaseModel): assets: list[AssetRead] = Field(..., title='Assets') id: UUID = Field(..., title='Id') type: EntityType | None = None - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') scan_parameters: dict[str, Any] = Field(..., title='Scan Parameters') input_recordings: list[NestedIonChannelRecordingRead] = Field( ..., title='Input Recordings' @@ -12811,6 +13067,8 @@ class IonChannelModelingConfigRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -12823,8 +13081,6 @@ class IonChannelModelingConfigRead(BaseModel): assets: list[AssetRead] = Field(..., title='Assets') id: UUID = Field(..., title='Id') type: EntityType | None = None - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') ion_channel_modeling_campaign_id: UUID = Field( ..., title='Ion Channel Modeling Campaign Id' ) @@ -12835,6 +13091,8 @@ class IonChannelRecordingRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -12870,8 +13128,6 @@ class IonChannelRecordingRead(BaseModel): description='Text provided by the data creators to inform users about data caveats, limitations, or required attribution practices.', title='Notice Text', ) - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') ljp: float = Field( default=0.0, description='Correction applied to the voltage trace, in mV', @@ -13260,6 +13516,8 @@ class CellMorphologyAnnotationExpandedRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -13295,10 +13553,10 @@ class CellMorphologyAnnotationExpandedRead(BaseModel): description='Text provided by the data creators to inform users about data caveats, limitations, or required attribution practices.', title='Notice Text', ) - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') location: PointLocationBase | None = None legacy_id: list[str] | None = Field(default=None, title='Legacy Id') + has_segmented_spines: bool = Field(default=False, title='Has Segmented Spines') + repair_pipeline_state: RepairPipelineType | None = None mtypes: list[AnnotationRead] | None = Field(..., title='Mtypes') cell_morphology_protocol: NestedCellMorphologyProtocolRead | None = None measurement_annotation: MeasurementAnnotationRead | None = None @@ -13308,6 +13566,8 @@ class CellMorphologyRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -13343,10 +13603,10 @@ class CellMorphologyRead(BaseModel): description='Text provided by the data creators to inform users about data caveats, limitations, or required attribution practices.', title='Notice Text', ) - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') location: PointLocationBase | None = None legacy_id: list[str] | None = Field(default=None, title='Legacy Id') + has_segmented_spines: bool = Field(default=False, title='Has Segmented Spines') + repair_pipeline_state: RepairPipelineType | None = None mtypes: list[AnnotationRead] | None = Field(..., title='Mtypes') cell_morphology_protocol: NestedCellMorphologyProtocolRead | None = None @@ -13355,6 +13615,8 @@ class EModelReadExpanded(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -13366,8 +13628,6 @@ class EModelReadExpanded(BaseModel): authorized_public: bool = Field(default=False, title='Authorized Public') creation_date: AwareDatetime = Field(..., title='Creation Date') update_date: AwareDatetime = Field(..., title='Update Date') - description: str = Field(..., title='Description') - name: str = Field(..., title='Name') iteration: str = Field(..., title='Iteration') score: float = Field(..., title='Score') seed: int = Field(..., title='Seed') @@ -13414,6 +13674,8 @@ class MEModelRead(BaseModel): model_config = ConfigDict( extra='allow', ) + name: str = Field(..., title='Name') + description: str = Field(..., title='Description') contributions: list[NestedContributionRead] | None = Field( ..., title='Contributions' ) @@ -13424,8 +13686,6 @@ class MEModelRead(BaseModel): authorized_public: bool = Field(default=False, title='Authorized Public') creation_date: AwareDatetime = Field(..., title='Creation Date') update_date: AwareDatetime = Field(..., title='Update Date') - name: str = Field(..., title='Name') - description: str = Field(..., title='Description') validation_status: ValidationStatus = Field( default_factory=lambda: ValidationStatus.model_validate('created') ) diff --git a/backend/src/neuroagent/tools/autogenerated_types/obione.py b/backend/src/neuroagent/tools/autogenerated_types/obione.py index 1e15e9566..204ad1248 100644 --- a/backend/src/neuroagent/tools/autogenerated_types/obione.py +++ b/backend/src/neuroagent/tools/autogenerated_types/obione.py @@ -161,6 +161,62 @@ class CircuitPopulationsResponse(BaseModel): populations: list[str] = Field(..., title='Populations') +class Info(BaseModel): + model_config = ConfigDict( + extra='ignore', + ) + type: Literal['Info'] = Field(..., title='Type') + campaign_name: str = Field( + ..., description='Name of the campaign.', min_length=1, title='Campaign Name' + ) + campaign_description: str = Field( + ..., + description='Description of the campaign.', + min_length=1, + title='Campaign Description', + ) + + +class Circuit1(RootModel[Circuit | CircuitFromID]): + root: Circuit | CircuitFromID = Field(..., discriminator='type') + + +class SimulationLength(RootModel[float]): + root: float = Field( + ..., + description='Simulation length in milliseconds (ms).', + ge=1.0, + le=5000.0, + title='Duration', + ) + + +class SimulationLength1Item(RootModel[float]): + root: float = Field(..., ge=1.0, le=5000.0) + + +class SimulationLength1(RootModel[list[SimulationLength1Item]]): + root: list[SimulationLength1Item] = Field( + ..., + description='Simulation length in milliseconds (ms).', + min_length=1, + title='Duration', + ) + + +class ExtracellularCalciumConcentrationItem(RootModel[float]): + root: float = Field(..., ge=0.0) + + +class ExtracellularCalciumConcentration(RootModel[float]): + root: float = Field( + ..., + description='Extracellular calcium concentration around the synapse in millimoles (mM). Increasing this value increases the probability of synaptic vesicle release, which in turn increases the level of network activity. In vivo values are estimated to be ~0.9-1.2mM, whilst in vitro values are on the order of 2mM.', + ge=0.0, + title='Extracellular Calcium Concentration', + ) + + class CircuitStatsLevelOfDetail(RootModel[Literal[0, 1, 2, 3]]): root: Literal[0, 1, 2, 3] = Field(..., title='CircuitStatsLevelOfDetail') @@ -394,6 +450,15 @@ class ConnectivityMetricsRequest(BaseModel): ) +class NeuronSet(BaseModel): + model_config = ConfigDict( + extra='ignore', + ) + block_dict_name: str = Field(default='', title='Block Dict Name') + block_name: str = Field(..., title='Block Name') + type: Literal['NeuronSetReference'] = Field(..., title='Type') + + class Duration(RootModel[float]): root: float = Field( ..., @@ -403,8 +468,8 @@ class Duration(RootModel[float]): ) -class DurationItem(RootModel[float]): - root: float = Field(..., ge=0.0) +class DurationItem(ExtracellularCalciumConcentrationItem): + pass class Contribution(BaseModel): @@ -480,6 +545,12 @@ class FloatRange(BaseModel): end: float = Field(..., title='End') +SourceNeuronSet = NeuronSet + + +TargetedNeuronSet = NeuronSet + + class GateExponents(BaseModel): model_config = ConfigDict( extra='ignore', @@ -520,22 +591,6 @@ class SamplePercentage7(RootModel[list[SamplePercentage7Item]]): ) -class Info(BaseModel): - model_config = ConfigDict( - extra='ignore', - ) - type: Literal['Info'] = Field(..., title='Type') - campaign_name: str = Field( - ..., description='Name of the campaign.', min_length=1, title='Campaign Name' - ) - campaign_description: str = Field( - ..., - description='Description of the campaign.', - min_length=1, - title='Campaign Description', - ) - - class SamplePercentage8(SamplePercentage): pass @@ -616,6 +671,57 @@ class MEModelFromID(BaseModel): type: Literal['MEModelFromID'] = Field(..., title='Type') +class Circuit2(RootModel[MEModelCircuit | MEModelFromID]): + root: MEModelCircuit | MEModelFromID = Field(..., discriminator='type') + + +class SimulationLength2(SimulationLength): + pass + + +class SimulationLength3Item(SimulationLength1Item): + pass + + +class SimulationLength3(RootModel[list[SimulationLength3Item]]): + root: list[SimulationLength3Item] = Field( + ..., + description='Simulation length in milliseconds (ms).', + min_length=1, + title='Duration', + ) + + +class Initialize2(BaseModel): + model_config = ConfigDict( + extra='ignore', + ) + type: Literal['MEModelSimulationScanConfig.Initialize'] = Field(..., title='Type') + circuit: MEModelCircuit | MEModelFromID | list[Circuit2] = Field( + ..., description='ME Model to simulate.', title='ME Model' + ) + simulation_length: SimulationLength2 | SimulationLength3 = Field( + default=1000.0, + description='Simulation length in milliseconds (ms).', + title='Duration', + ) + extracellular_calcium_concentration: ( + list[ExtracellularCalciumConcentrationItem] | ExtracellularCalciumConcentration + ) = Field( + default=1.1, + description='Extracellular calcium concentration around the synapse in millimoles (mM). Increasing this value increases the probability of synaptic vesicle release, which in turn increases the level of network activity. In vivo values are estimated to be ~0.9-1.2mM, whilst in vitro values are on the order of 2mM.', + title='Extracellular Calcium Concentration', + ) + v_init: list[float] | float = Field( + default=-80.0, + description='Initial membrane potential in millivolts (mV).', + title='Initial Voltage', + ) + random_seed: list[int] | int = Field( + default=1, description='Random seed for the simulation.', title='Random Seed' + ) + + class MEModelWithSynapsesCircuit(BaseModel): model_config = ConfigDict( extra='ignore', @@ -636,6 +742,67 @@ class MEModelWithSynapsesCircuitFromID(BaseModel): type: Literal['MEModelWithSynapsesCircuitFromID'] = Field(..., title='Type') +class Circuit3( + RootModel[MEModelWithSynapsesCircuit | MEModelWithSynapsesCircuitFromID] +): + root: MEModelWithSynapsesCircuit | MEModelWithSynapsesCircuitFromID = Field( + ..., discriminator='type' + ) + + +class SimulationLength4(SimulationLength): + pass + + +class SimulationLength5Item(SimulationLength1Item): + pass + + +class SimulationLength5(RootModel[list[SimulationLength5Item]]): + root: list[SimulationLength5Item] = Field( + ..., + description='Simulation length in milliseconds (ms).', + min_length=1, + title='Duration', + ) + + +class Initialize3(BaseModel): + model_config = ConfigDict( + extra='ignore', + ) + type: Literal['MEModelWithSynapsesCircuitSimulationScanConfig.Initialize'] = Field( + ..., title='Type' + ) + circuit: ( + MEModelWithSynapsesCircuit | MEModelWithSynapsesCircuitFromID | list[Circuit3] + ) = Field( + ..., + description='MEModel with synapses to simulate.', + title='MEModel With Synapses', + ) + simulation_length: SimulationLength4 | SimulationLength5 = Field( + default=1000.0, + description='Simulation length in milliseconds (ms).', + title='Duration', + ) + extracellular_calcium_concentration: ( + list[ExtracellularCalciumConcentrationItem] | ExtracellularCalciumConcentration + ) = Field( + default=1.1, + description='Extracellular calcium concentration around the synapse in millimoles (mM). Increasing this value increases the probability of synaptic vesicle release, which in turn increases the level of network activity. In vivo values are estimated to be ~0.9-1.2mM, whilst in vitro values are on the order of 2mM.', + title='Extracellular Calcium Concentration', + ) + v_init: list[float] | float = Field( + default=-80.0, + description='Initial membrane potential in millivolts (mV).', + title='Initial Voltage', + ) + random_seed: list[int] | int = Field( + default=1, description='Random seed for the simulation.', title='Random Seed' + ) + + class MTypeClassification(BaseModel): model_config = ConfigDict( extra='ignore', @@ -811,7 +978,7 @@ class NeuronPropertyFilter(BaseModel): model_config = ConfigDict( extra='ignore', ) - filter_dict: dict[str, list] = Field( + filter_dict: dict[str, list[Any]] = Field( default={}, description="Filter dictionary. Note as this is NOT a Block and the list here is not to support multi-dimensional parameters but to support a key-value pair with multiple values i.e. {'layer': ['2', '3']}}", title='Filter Dict', @@ -819,13 +986,7 @@ class NeuronPropertyFilter(BaseModel): type: Literal['NeuronPropertyFilter'] = Field(..., title='Type') -class NeuronSetReference(BaseModel): - model_config = ConfigDict( - extra='ignore', - ) - block_dict_name: str = Field(default='', title='Block Dict Name') - block_name: str = Field(..., title='Block Name') - type: Literal['NeuronSetReference'] = Field(..., title='Type') +NeuronSetReference = NeuronSet class NodePopulationType(RootModel[Literal['biophysical', 'virtual']]): @@ -861,7 +1022,7 @@ class Variance(RootModel[float]): ) -class VarianceItem(DurationItem): +class VarianceItem(ExtracellularCalciumConcentrationItem): pass @@ -1177,7 +1338,7 @@ class StartTime(RootModel[float]): ) -class StartTimeItem(DurationItem): +class StartTimeItem(ExtracellularCalciumConcentrationItem): pass @@ -1190,7 +1351,7 @@ class Interval(RootModel[float]): ) -class IntervalItem(DurationItem): +class IntervalItem(ExtracellularCalciumConcentrationItem): pass @@ -1233,7 +1394,7 @@ class Duration6(Duration): pass -class DurationItem6(DurationItem): +class DurationItem6(ExtracellularCalciumConcentrationItem): pass @@ -1246,7 +1407,7 @@ class PercentageOfThresholdCurrent(RootModel[float]): ) -class PercentageOfThresholdCurrentItem(DurationItem): +class PercentageOfThresholdCurrentItem(ExtracellularCalciumConcentrationItem): pass @@ -1259,7 +1420,7 @@ class PercentageOfThresholdCurrentStart(RootModel[float]): ) -class PercentageOfThresholdCurrentStartItem(DurationItem): +class PercentageOfThresholdCurrentStartItem(ExtracellularCalciumConcentrationItem): pass @@ -1272,7 +1433,7 @@ class PercentageOfThresholdCurrentEnd(RootModel[float]): ) -class PercentageOfThresholdCurrentEndItem(DurationItem): +class PercentageOfThresholdCurrentEndItem(ExtracellularCalciumConcentrationItem): pass @@ -1285,7 +1446,7 @@ class MeanPercentageOfThresholdCurrent(RootModel[float]): ) -class MeanPercentageOfThresholdCurrentItem(DurationItem): +class MeanPercentageOfThresholdCurrentItem(ExtracellularCalciumConcentrationItem): pass @@ -1298,7 +1459,7 @@ class UseScaling(RootModel[float]): ) -class UseScalingItem(DurationItem): +class UseScalingItem(ExtracellularCalciumConcentrationItem): pass @@ -1642,13 +1803,16 @@ class Dt1(RootModel[float]): ) +Dt2 = NonNegativeFloatRange + + class SomaVoltageRecording(BaseModel): model_config = ConfigDict( extra='ignore', ) type: Literal['SomaVoltageRecording'] = Field(..., title='Type') neuron_set: NeuronSetReference | None = None - dt: Dt1 | list[DtItem] | NonNegativeFloatRange = Field( + dt: Dt1 | list[DtItem] | Dt2 = Field( default=0.1, description='Interval between recording time steps in milliseconds (ms).', title='Timestep', @@ -1705,7 +1869,7 @@ class Duration11(Duration): pass -class DurationItem11(DurationItem): +class DurationItem11(ExtracellularCalciumConcentrationItem): pass @@ -1718,7 +1882,7 @@ class MagnesiumValue(RootModel[float]): ) -class MagnesiumValueItem(DurationItem): +class MagnesiumValueItem(ExtracellularCalciumConcentrationItem): pass @@ -1748,6 +1912,13 @@ class ThermoFitMTauV2(BaseModel): type: Literal['ThermoFitMTauV2'] = Field(..., title='Type') +class Dt3(Dt1): + pass + + +Dt4 = NonNegativeFloatRange + + class StartTime2(RootModel[float]): root: float = Field( ..., @@ -1766,7 +1937,7 @@ class EndTime(RootModel[float]): ) -class EndTimeItem(DurationItem): +class EndTimeItem(ExtracellularCalciumConcentrationItem): pass @@ -1776,7 +1947,7 @@ class TimeWindowSomaVoltageRecording(BaseModel): ) type: Literal['TimeWindowSomaVoltageRecording'] = Field(..., title='Type') neuron_set: NeuronSetReference | None = None - dt: Dt1 | list[DtItem] | NonNegativeFloatRange = Field( + dt: Dt3 | list[DtItem] | Dt4 = Field( default=0.1, description='Interval between recording time steps in milliseconds (ms).', title='Timestep', @@ -1844,8 +2015,8 @@ class NItem(Element): pass -class ColumnsXyz(RootModel[list]): - root: list = Field( +class ColumnsXyz(RootModel[list[Any]]): + root: list[Any] = Field( ..., description='Names of the three neuron (node) properties used for volumetric tests', max_length=3, @@ -1854,8 +2025,8 @@ class ColumnsXyz(RootModel[list]): ) -class ColumnsXyzItem(RootModel[list]): - root: list = Field(..., max_length=3, min_length=3) +class ColumnsXyzItem(RootModel[list[Any]]): + root: list[Any] = Field(..., max_length=3, min_length=3) class VolumetricCountNeuronSet(BaseModel): @@ -1897,7 +2068,7 @@ class VolumetricCountNeuronSet(BaseModel): ) n: N | list[NItem] = Field(..., description='Number of neurons to find', title='N') columns_xyz: ColumnsXyz | list[ColumnsXyzItem] = Field( - default=['x', 'y', 'z'], + default_factory=lambda: ColumnsXyz.model_validate(['x', 'y', 'z']), description='Names of the three neuron (node) properties used for volumetric tests', title='Columns Xyz', ) @@ -1934,7 +2105,7 @@ class Radius(RootModel[float]): ) -class Radiu(DurationItem): +class Radiu(ExtracellularCalciumConcentrationItem): pass @@ -1979,7 +2150,7 @@ class VolumetricRadiusNeuronSet(BaseModel): ..., description='Radius in um of volumetric sample', title='Radius' ) columns_xyz: ColumnsXyz | list[ColumnsXyzItem] = Field( - default=['x', 'y', 'z'], + default_factory=lambda: ColumnsXyz.model_validate(['x', 'y', 'z']), description='Names of the three neuron (node) properties used for volumetric tests', title='Columns Xyz', ) @@ -2228,26 +2399,20 @@ class ObiOneScientificTasksFolderCompressionFolderCompressionScanConfigInitializ ) -class Circuit1(RootModel[Circuit | CircuitFromID]): - root: Circuit | CircuitFromID = Field(..., discriminator='type') +class Circuit4(Circuit1): + pass -class SimulationLength(RootModel[float]): - root: float = Field( - ..., - description='Simulation length in milliseconds (ms).', - ge=1.0, - le=5000.0, - title='Duration', - ) +class SimulationLength6(SimulationLength): + pass -class SimulationLength1Item(RootModel[float]): - root: float = Field(..., ge=1.0, le=5000.0) +class SimulationLength7Item(SimulationLength1Item): + pass -class SimulationLength1(RootModel[list[SimulationLength1Item]]): - root: list[SimulationLength1Item] = Field( +class SimulationLength7(RootModel[list[SimulationLength7Item]]): + root: list[SimulationLength7Item] = Field( ..., description='Simulation length in milliseconds (ms).', min_length=1, @@ -2255,19 +2420,6 @@ class SimulationLength1(RootModel[list[SimulationLength1Item]]): ) -class ExtracellularCalciumConcentrationItem(DurationItem): - pass - - -class ExtracellularCalciumConcentration(RootModel[float]): - root: float = Field( - ..., - description='Extracellular calcium concentration around the synapse in millimoles (mM). Increasing this value increases the probability of synaptic vesicle release, which in turn increases the level of network activity. In vivo values are estimated to be ~0.9-1.2mM, whilst in vitro values are on the order of 2mM.', - ge=0.0, - title='Extracellular Calcium Concentration', - ) - - class ObiOneScientificTasksGenerateSimulationConfigsCircuitSimulationScanConfigInitialize( BaseModel ): @@ -2275,10 +2427,10 @@ class ObiOneScientificTasksGenerateSimulationConfigsCircuitSimulationScanConfigI extra='ignore', ) type: Literal['CircuitSimulationScanConfig.Initialize'] = Field(..., title='Type') - circuit: Circuit | CircuitFromID | list[Circuit1] = Field( + circuit: Circuit | CircuitFromID | list[Circuit4] = Field( ..., description='Circuit to simulate.', title='Circuit' ) - simulation_length: SimulationLength | SimulationLength1 = Field( + simulation_length: SimulationLength6 | SimulationLength7 = Field( default=1000.0, description='Simulation length in milliseconds (ms).', title='Duration', @@ -2301,20 +2453,20 @@ class ObiOneScientificTasksGenerateSimulationConfigsCircuitSimulationScanConfigI node_set: NeuronSetReference | None = None -class Circuit2(RootModel[MEModelCircuit | MEModelFromID]): - root: MEModelCircuit | MEModelFromID = Field(..., discriminator='type') +class Circuit5(Circuit2): + pass -class SimulationLength2(SimulationLength): +class SimulationLength8(SimulationLength): pass -class SimulationLength3Item(SimulationLength1Item): +class SimulationLength9Item(SimulationLength1Item): pass -class SimulationLength3(RootModel[list[SimulationLength3Item]]): - root: list[SimulationLength3Item] = Field( +class SimulationLength9(RootModel[list[SimulationLength9Item]]): + root: list[SimulationLength9Item] = Field( ..., description='Simulation length in milliseconds (ms).', min_length=1, @@ -2329,10 +2481,10 @@ class ObiOneScientificTasksGenerateSimulationConfigsMEModelSimulationScanConfigI extra='ignore', ) type: Literal['MEModelSimulationScanConfig.Initialize'] = Field(..., title='Type') - circuit: MEModelCircuit | MEModelFromID | list[Circuit2] = Field( + circuit: MEModelCircuit | MEModelFromID | list[Circuit5] = Field( ..., description='ME Model to simulate.', title='ME Model' ) - simulation_length: SimulationLength2 | SimulationLength3 = Field( + simulation_length: SimulationLength8 | SimulationLength9 = Field( default=1000.0, description='Simulation length in milliseconds (ms).', title='Duration', @@ -2354,24 +2506,20 @@ class ObiOneScientificTasksGenerateSimulationConfigsMEModelSimulationScanConfigI ) -class Circuit3( - RootModel[MEModelWithSynapsesCircuit | MEModelWithSynapsesCircuitFromID] -): - root: MEModelWithSynapsesCircuit | MEModelWithSynapsesCircuitFromID = Field( - ..., discriminator='type' - ) +class Circuit6(Circuit3): + pass -class SimulationLength4(SimulationLength): +class SimulationLength10(SimulationLength): pass -class SimulationLength5Item(SimulationLength1Item): +class SimulationLength11Item(SimulationLength1Item): pass -class SimulationLength5(RootModel[list[SimulationLength5Item]]): - root: list[SimulationLength5Item] = Field( +class SimulationLength11(RootModel[list[SimulationLength11Item]]): + root: list[SimulationLength11Item] = Field( ..., description='Simulation length in milliseconds (ms).', min_length=1, @@ -2389,13 +2537,13 @@ class ObiOneScientificTasksGenerateSimulationConfigsMEModelWithSynapsesCircuitSi ..., title='Type' ) circuit: ( - MEModelWithSynapsesCircuit | MEModelWithSynapsesCircuitFromID | list[Circuit3] + MEModelWithSynapsesCircuit | MEModelWithSynapsesCircuitFromID | list[Circuit6] ) = Field( ..., description='MEModel with synapses to simulate.', title='MEModel With Synapses', ) - simulation_length: SimulationLength4 | SimulationLength5 = Field( + simulation_length: SimulationLength10 | SimulationLength11 = Field( default=1000.0, description='Simulation length in milliseconds (ms).', title='Duration', @@ -2493,20 +2641,20 @@ class ObiOneScientificTasksMorphologyMetricsMorphologyMetricsScanConfigInitializ ) -class Circuit4(Circuit1): +class Circuit7(Circuit1): pass -class SimulationLength6(SimulationLength): +class SimulationLength12(SimulationLength): pass -class SimulationLength7Item(SimulationLength1Item): +class SimulationLength13Item(SimulationLength1Item): pass -class SimulationLength7(RootModel[list[SimulationLength7Item]]): - root: list[SimulationLength7Item] = Field( +class SimulationLength13(RootModel[list[SimulationLength13Item]]): + root: list[SimulationLength13Item] = Field( ..., description='Simulation length in milliseconds (ms).', min_length=1, @@ -2519,10 +2667,10 @@ class ObiOneScientificUnionsAliasesSimulationsFormInitialize(BaseModel): extra='ignore', ) type: Literal['SimulationsForm.Initialize'] = Field(..., title='Type') - circuit: Circuit | CircuitFromID | list[Circuit4] = Field( + circuit: Circuit | CircuitFromID | list[Circuit7] = Field( ..., description='Circuit to simulate.', title='Circuit' ) - simulation_length: SimulationLength6 | SimulationLength7 = Field( + simulation_length: SimulationLength12 | SimulationLength13 = Field( default=1000.0, description='Simulation length in milliseconds (ms).', title='Duration', @@ -2847,6 +2995,37 @@ class CircuitMetricsOutput(BaseModel): ) +class Initialize(BaseModel): + model_config = ConfigDict( + extra='ignore', + ) + type: Literal['CircuitSimulationScanConfig.Initialize'] = Field(..., title='Type') + circuit: Circuit | CircuitFromID | list[Circuit1] = Field( + ..., description='Circuit to simulate.', title='Circuit' + ) + simulation_length: SimulationLength | SimulationLength1 = Field( + default=1000.0, + description='Simulation length in milliseconds (ms).', + title='Duration', + ) + extracellular_calcium_concentration: ( + list[ExtracellularCalciumConcentrationItem] | ExtracellularCalciumConcentration + ) = Field( + default=1.1, + description='Extracellular calcium concentration around the synapse in millimoles (mM). Increasing this value increases the probability of synaptic vesicle release, which in turn increases the level of network activity. In vivo values are estimated to be ~0.9-1.2mM, whilst in vitro values are on the order of 2mM.', + title='Extracellular Calcium Concentration', + ) + v_init: list[float] | float = Field( + default=-80.0, + description='Initial membrane potential in millivolts (mV).', + title='Initial Voltage', + ) + random_seed: list[int] | int = Field( + default=1, description='Random seed for the simulation.', title='Random Seed' + ) + node_set: NeuronSetReference | None = None + + class ConnectivityMatrixExtractionScanConfig(BaseModel): model_config = ConfigDict( extra='ignore', @@ -2861,7 +3040,7 @@ class ConstantCurrentClampSomaticStimulus(BaseModel): ) type: Literal['ConstantCurrentClampSomaticStimulus'] = Field(..., title='Type') timestamps: TimestampsReference | None = None - neuron_set: NeuronSetReference | None = None + neuron_set: NeuronSet | None = None timestamp_offset: float | list[float] | None = Field( default=0.0, description='The offset of the stimulus relative to each timestamp in milliseconds (ms).', @@ -2946,8 +3125,8 @@ class FullySynchronousSpikeStimulus(BaseModel): ) type: Literal['FullySynchronousSpikeStimulus'] = Field(..., title='Type') timestamps: TimestampsReference | None = None - source_neuron_set: NeuronSetReference | None = None - targeted_neuron_set: NeuronSetReference | None = None + source_neuron_set: SourceNeuronSet | None = None + targeted_neuron_set: TargetedNeuronSet | None = None timestamp_offset: float | list[float] | None = Field( default=0.0, description='The offset of the stimulus relative to each timestamp in milliseconds (ms).', @@ -2970,7 +3149,7 @@ class HyperpolarizingCurrentClampSomaticStimulus(BaseModel): ..., title='Type' ) timestamps: TimestampsReference | None = None - neuron_set: NeuronSetReference | None = None + neuron_set: NeuronSet | None = None timestamp_offset: float | list[float] | None = Field( default=0.0, description='The offset of the stimulus relative to each timestamp in milliseconds (ms).', @@ -3003,14 +3182,17 @@ class IDNeuronSet(BaseModel): neuron_ids: NamedTuple | NeuronIds = Field(..., title='Neuron Ids') +Initialize1 = ( + ObiOneScientificTasksIonChannelModelingIonChannelFittingScanConfigInitialize +) + + class IonChannelFittingScanConfig(BaseModel): model_config = ConfigDict( extra='ignore', ) type: Literal['IonChannelFittingScanConfig'] = Field(..., title='Type') - initialize: ( - ObiOneScientificTasksIonChannelModelingIonChannelFittingScanConfigInitialize - ) = Field( + initialize: Initialize1 = Field( ..., description='Parameters for initializing the simulation.', title='Initialization', @@ -3052,7 +3234,7 @@ class LinearCurrentClampSomaticStimulus(BaseModel): ) type: Literal['LinearCurrentClampSomaticStimulus'] = Field(..., title='Type') timestamps: TimestampsReference | None = None - neuron_set: NeuronSetReference | None = None + neuron_set: NeuronSet | None = None timestamp_offset: float | list[float] | None = Field( default=0.0, description='The offset of the stimulus relative to each timestamp in milliseconds (ms).', @@ -3130,7 +3312,7 @@ class MultiPulseCurrentClampSomaticStimulus(BaseModel): ) type: Literal['MultiPulseCurrentClampSomaticStimulus'] = Field(..., title='Type') timestamps: TimestampsReference | None = None - neuron_set: NeuronSetReference | None = None + neuron_set: NeuronSet | None = None timestamp_offset: float | list[float] | None = Field( default=0.0, description='The offset of the stimulus relative to each timestamp in milliseconds (ms).', @@ -3166,7 +3348,7 @@ class NormallyDistributedCurrentClampSomaticStimulus(BaseModel): ..., title='Type' ) timestamps: TimestampsReference | None = None - neuron_set: NeuronSetReference | None = None + neuron_set: NeuronSet | None = None timestamp_offset: float | list[float] | None = Field( default=0.0, description='The offset of the stimulus relative to each timestamp in milliseconds (ms).', @@ -3195,8 +3377,8 @@ class PoissonSpikeStimulus(BaseModel): ) type: Literal['PoissonSpikeStimulus'] = Field(..., title='Type') timestamps: TimestampsReference | None = None - source_neuron_set: NeuronSetReference | None = None - targeted_neuron_set: NeuronSetReference | None = None + source_neuron_set: SourceNeuronSet | None = None + targeted_neuron_set: TargetedNeuronSet | None = None timestamp_offset: float | list[float] | None = Field( default=0.0, description='The offset of the stimulus relative to each timestamp in milliseconds (ms).', @@ -3227,7 +3409,7 @@ class RelativeConstantCurrentClampSomaticStimulus(BaseModel): ..., title='Type' ) timestamps: TimestampsReference | None = None - neuron_set: NeuronSetReference | None = None + neuron_set: NeuronSet | None = None timestamp_offset: float | list[float] | None = Field( default=0.0, description='The offset of the stimulus relative to each timestamp in milliseconds (ms).', @@ -3255,7 +3437,7 @@ class RelativeLinearCurrentClampSomaticStimulus(BaseModel): ..., title='Type' ) timestamps: TimestampsReference | None = None - neuron_set: NeuronSetReference | None = None + neuron_set: NeuronSet | None = None timestamp_offset: float | list[float] | None = Field( default=0.0, description='The offset of the stimulus relative to each timestamp in milliseconds (ms).', @@ -3290,7 +3472,7 @@ class RelativeNormallyDistributedCurrentClampSomaticStimulus(BaseModel): ..., title='Type' ) timestamps: TimestampsReference | None = None - neuron_set: NeuronSetReference | None = None + neuron_set: NeuronSet | None = None timestamp_offset: float | list[float] | None = Field( default=0.0, description='The offset of the stimulus relative to each timestamp in milliseconds (ms).', @@ -3321,7 +3503,7 @@ class SinusoidalCurrentClampSomaticStimulus(BaseModel): ) type: Literal['SinusoidalCurrentClampSomaticStimulus'] = Field(..., title='Type') timestamps: TimestampsReference | None = None - neuron_set: NeuronSetReference | None = None + neuron_set: NeuronSet | None = None timestamp_offset: float | list[float] | None = Field( default=0.0, description='The offset of the stimulus relative to each timestamp in milliseconds (ms).', @@ -3355,8 +3537,8 @@ class SinusoidalPoissonSpikeStimulus(BaseModel): ) type: Literal['SinusoidalPoissonSpikeStimulus'] = Field(..., title='Type') timestamps: TimestampsReference | None = None - source_neuron_set: NeuronSetReference | None = None - targeted_neuron_set: NeuronSetReference | None = None + source_neuron_set: SourceNeuronSet | None = None + targeted_neuron_set: TargetedNeuronSet | None = None timestamp_offset: float | list[float] | None = Field( default=0.0, description='The offset of the stimulus relative to each timestamp in milliseconds (ms).', @@ -3402,7 +3584,7 @@ class SubthresholdCurrentClampSomaticStimulus(BaseModel): ) type: Literal['SubthresholdCurrentClampSomaticStimulus'] = Field(..., title='Type') timestamps: TimestampsReference | None = None - neuron_set: NeuronSetReference | None = None + neuron_set: NeuronSet | None = None timestamp_offset: float | list[float] | None = Field( default=0.0, description='The offset of the stimulus relative to each timestamp in milliseconds (ms).', @@ -3484,7 +3666,7 @@ class CircuitSimulationScanConfig(BaseModel): description='Synaptic manipulations for the simulation.', title='Synaptic Manipulations', ) - initialize: ObiOneScientificTasksGenerateSimulationConfigsCircuitSimulationScanConfigInitialize = Field( + initialize: Initialize = Field( ..., description='Parameters for initializing the simulation.', title='Initialization', @@ -3524,7 +3706,7 @@ class MEModelSimulationScanConfig(BaseModel): default=None, description='Recordings for the simulation.', title='Recordings' ) info: Info = Field(..., description='Information about the simulation campaign.') - initialize: ObiOneScientificTasksGenerateSimulationConfigsMEModelSimulationScanConfigInitialize = Field( + initialize: Initialize2 = Field( ..., description='Parameters for initializing the simulation.', title='Initialization', @@ -3574,7 +3756,7 @@ class MEModelWithSynapsesCircuitSimulationScanConfig(BaseModel): description='Synaptic manipulations for the simulation.', title='Synaptic Manipulations', ) - initialize: ObiOneScientificTasksGenerateSimulationConfigsMEModelWithSynapsesCircuitSimulationScanConfigInitialize = Field( + initialize: Initialize3 = Field( ..., description='Parameters for initializing the simulation.', title='Initialization', diff --git a/backend/src/neuroagent/tools/base_tool.py b/backend/src/neuroagent/tools/base_tool.py index 098e3b212..790231882 100644 --- a/backend/src/neuroagent/tools/base_tool.py +++ b/backend/src/neuroagent/tools/base_tool.py @@ -166,29 +166,26 @@ class BaseTool(BaseModel, ABC): @classmethod def pydantic_to_openai_schema(cls) -> dict[str, Any]: - """Convert pydantic schema to OpenAI json.""" + """Convert pydantic schema to OpenAI function JSON schema.""" + # Get the schema from json_schema attribute or input_schema model if cls.json_schema is not None: - parameters = cls.json_schema + schema = cls.json_schema else: - parameters = cls.__annotations__["input_schema"].model_json_schema() + schema = cls.__annotations__["input_schema"].model_json_schema() - # The name and description are duplicated to accomodate for - # models compatible with flat and nested JSON schema. - # E.g. o3 is flattened JSON schema compatible only - new_retval: dict[str, Any] = { + # Wrap in standard JSON Schema structure if needed + if "type" not in schema and "properties" not in schema: + schema = {"type": "object", "properties": schema} + + # Ensure additionalProperties is False + schema.setdefault("additionalProperties", False) + + return { "type": "function", "name": cls.name, "description": cls.description, - "function": { - "name": cls.name, - "description": cls.description, - "strict": False, - "parameters": parameters, - }, + "parameters": schema, } - new_retval["function"]["parameters"]["additionalProperties"] = False - - return new_retval @abstractmethod async def arun(self) -> BaseModel: diff --git a/backend/src/neuroagent/tools/circuit_population_analysis_tool.py b/backend/src/neuroagent/tools/circuit_population_analysis_tool.py index ebe0368ec..f27a432f1 100644 --- a/backend/src/neuroagent/tools/circuit_population_analysis_tool.py +++ b/backend/src/neuroagent/tools/circuit_population_analysis_tool.py @@ -314,19 +314,16 @@ async def arun(self) -> CircuitPopulationAnalysisOutput: # Get SQL from OpenAI model = "gpt-4o-mini" - response = ( - await self.metadata.openai_client.beta.chat.completions.parse( - model=model, - messages=[ - {"role": "system", "content": system_prompt}, - {"role": "user", "content": user_prompt}, - ], - response_format=SQLStatement, - ) + response = await self.metadata.openai_client.responses.parse( + instructions=system_prompt, + input=user_prompt, + model=model, + text_format=SQLStatement, + store=False, ) - if response.choices[0].message.parsed: - sql = response.choices[0].message.parsed.sql_statement + if response.output_parsed and response.output_parsed.sql_statement: + sql = response.output_parsed.sql_statement else: raise ValueError("Couldn't generate SQL statement.") diff --git a/backend/src/neuroagent/tools/obione_generatesimulationsconfig.py b/backend/src/neuroagent/tools/obione_generatesimulationsconfig.py index bba021f98..8802acb92 100644 --- a/backend/src/neuroagent/tools/obione_generatesimulationsconfig.py +++ b/backend/src/neuroagent/tools/obione_generatesimulationsconfig.py @@ -123,18 +123,17 @@ async def arun(self) -> SimulationsForm: """ model = "gpt-5-mini" # Then generate the global class and make the according references - response = await self.metadata.openai_client.beta.chat.completions.parse( - messages=[ - {"role": "system", "content": system_prompt}, - {"role": "user", "content": self.input_schema.config_request}, - ], + response = await self.metadata.openai_client.responses.parse( + instructions=system_prompt, + input=self.input_schema.config_request, model=model, - reasoning_effort="medium", - response_format=SimulationsFormModified, + text_format=SimulationsFormModified, + reasoning={"effort": "medium"}, + store=False, ) - if response.choices[0].message.parsed: + if response.output_parsed: # Get the output config - config = response.choices[0].message.parsed + config = response.output_parsed # Gather everything in the OBI-One compatible class output_config = SimulationsForm( diff --git a/backend/src/neuroagent/utils.py b/backend/src/neuroagent/utils.py index 85843ec26..941a5b233 100644 --- a/backend/src/neuroagent/utils.py +++ b/backend/src/neuroagent/utils.py @@ -1,69 +1,43 @@ """Utilies for neuroagent.""" +import copy import json import logging import re import uuid from typing import Any, Literal -from fastapi import HTTPException -from openai.types.completion_usage import CompletionUsage +from openai.types.responses import ( + ResponseFunctionToolCallOutputItem, + ResponseOutputItem, + ResponseUsage, +) from neuroagent.app.database.sql_schemas import ( - Entity, Messages, + Parts, + PartType, + Task, + TokenConsumption, + TokenType, ) logger = logging.getLogger(__name__) -def merge_fields(target: dict[str, Any], source: dict[str, Any]) -> None: - """Recursively merge each field in the target dictionary.""" - for key, value in source.items(): - if isinstance(value, str): - target[key] += value - elif value is not None and isinstance(value, dict): - merge_fields(target[key], value) - - -def merge_chunk(final_response: dict[str, Any], delta: dict[str, Any]) -> None: - """Merge a chunk into the final message.""" - delta.pop("role", None) - merge_fields(final_response, delta) - - tool_calls = delta.get("tool_calls") - if tool_calls and len(tool_calls) > 0: - for tool_call in tool_calls: - index = tool_call.pop("index") - if final_response["tool_calls"][index]["type"]: - tool_call["type"] = None - merge_fields(final_response["tool_calls"][index], tool_call) - - async def messages_to_openai_content( db_messages: list[Messages] | None = None, ) -> list[dict[str, Any]]: """Exctract content from Messages as dictionary to pass them to OpenAI.""" - messages = [] + # Maybe we should add a check to see if the parts where awaited + openai_messages = [] if db_messages: for msg in db_messages: - messages.append(json.loads(msg.content)) - - return messages + for part in msg.parts: + openai_messages.append(copy.deepcopy(part.output)) + # to prevent replacing output in tool filtering - -def get_entity(message: dict[str, Any]) -> Entity: - """Define the Enum entity of the message based on its content.""" - if message["role"] == "user": - return Entity.USER - elif message["role"] == "tool": - return Entity.TOOL - elif message["role"] == "assistant" and message.get("tool_calls", False): - return Entity.AI_TOOL - elif message["role"] == "assistant" and not message.get("tool_calls", False): - return Entity.AI_MESSAGE - else: - raise HTTPException(status_code=500, detail="Unknown message entity.") + return openai_messages def complete_partial_json(partial: str) -> str: @@ -250,19 +224,19 @@ def delete_from_storage( objects_to_delete = [] -def get_token_count(usage: CompletionUsage | None) -> dict[str, int | None]: +def get_token_count(usage: ResponseUsage | None) -> dict[str, int | None]: """Assign token count to a message given a usage chunk.""" # Parse usage to add to message's data if usage: # Compute input, input_cached, completion - input_tokens = usage.prompt_tokens + input_tokens = usage.input_tokens cached_tokens = ( - usage.prompt_tokens_details.cached_tokens - if usage.prompt_tokens_details + usage.input_tokens_details.cached_tokens + if usage.input_tokens_details else None ) prompt_tokens = input_tokens - cached_tokens if cached_tokens else input_tokens - completion_tokens = usage.completion_tokens + completion_tokens = usage.output_tokens return { "input_cached": cached_tokens, @@ -271,3 +245,105 @@ def get_token_count(usage: CompletionUsage | None) -> dict[str, int | None]: } else: return {"input_cached": None, "input_noncached": None, "completion": None} + + +def append_part( + message: Messages, + history: list[dict[str, Any]], + openai_part: ResponseOutputItem | ResponseFunctionToolCallOutputItem, + type: PartType, + is_complete: bool = True, +) -> None: + """Create a reasoning part and append it to the message and history.""" + if type == PartType.REASONING: + # Openai does not like none for status ... and it outputs none in reasoning ... + output = openai_part.model_dump(exclude={"status"}) + else: + output = openai_part.model_dump() + + part = Parts( + message_id=message.message_id, + order_index=len(message.parts), + type=type, + output=output, + is_complete=is_complete, + ) + message.parts.append(part) + history.append(output) + + +def get_main_LLM_token_consumption( + usage_data: ResponseUsage | None, model: str, task: Task +) -> list[TokenConsumption]: + """Create token consumption objects from usage data.""" + if not usage_data: + return [] + + input_cached = ( + getattr( + getattr(usage_data, "input_tokens_details", 0), + "cached_tokens", + 0, + ) + or 0 + ) + input_noncached = getattr(usage_data, "input_tokens", 0) - input_cached + completion_tokens = getattr(usage_data, "output_tokens", 0) or 0 + + return [ + TokenConsumption( + type=token_type, + task=task, + count=count, + model=model, + ) + for token_type, count in [ + (TokenType.INPUT_CACHED, input_cached), + (TokenType.INPUT_NONCACHED, input_noncached), + (TokenType.COMPLETION, completion_tokens), + ] + if count + ] + + +def get_tool_token_consumption( + tool_response: ResponseFunctionToolCallOutputItem, + context_variables: dict[str, Any], +) -> list[TokenConsumption]: + """Get token consumption for a tool response.""" + if context_variables["usage_dict"].get(tool_response.call_id): + tool_call_consumption = context_variables["usage_dict"][tool_response.call_id] + return [ + TokenConsumption( + type=token_type, + task=Task.CALL_WITHIN_TOOL, + count=count, + model=tool_call_consumption["model"], + ) + for token_type, count in [ + (TokenType.INPUT_CACHED, tool_call_consumption["input_cached"]), + (TokenType.INPUT_NONCACHED, tool_call_consumption["input_noncached"]), + (TokenType.COMPLETION, tool_call_consumption["completion"]), + ] + if count + ] + return [] + + +def get_previous_hil_metadata( + message: Messages, tool_map: dict[str, Any] +) -> list[dict[str, Any]]: + """Initialize metadata for previous HIL tool calls.""" + metadata_data = [] + for part in message.parts: + if part.type == PartType.FUNCTION_CALL: + tool_name = part.output.get("name") + if tool_name and tool_map.get(tool_name) and tool_map[tool_name].hil: + metadata_data.append( + { + "toolCallId": part.output.get("call_id"), + "validated": "accepted" if part.validated else "rejected", + "isComplete": part.is_complete, + } + ) + return metadata_data diff --git a/backend/tests/app/routers/test_qa.py b/backend/tests/app/routers/test_qa.py index 6ae061112..49c284ed4 100644 --- a/backend/tests/app/routers/test_qa.py +++ b/backend/tests/app/routers/test_qa.py @@ -123,19 +123,19 @@ def test_question_suggestions( assert response.json() == mock_class_response.model_dump() # At the end we check if the calls were made with the right arguments: - call_list = mock_openai_client.beta.chat.completions.parse.call_args_list - assert call_list[0].kwargs["messages"][1] == { - "role": "user", - "content": "USER JOURNEY: \n[{'timestamp': '1970-01-02T10:17:36Z', 'region': 'Amzing BR', 'artifact': 'Super artifact'}]", - } - assert call_list[1].kwargs["messages"][1] == { - "role": "user", - "content": "USER JOURNEY: \n[{'timestamp': '1970-01-02T10:17:36Z', 'region': 'Amzing BR', 'artifact': 'Super artifact'}]", - } - assert call_list[2].kwargs["messages"][1] == { - "role": "user", - "content": 'CONVERSATION MESSAGES: \n[{"content": "This is my query."}, {"content": "sample response content."}]', - } + call_list = mock_openai_client.responses.parse.call_args_list + assert ( + call_list[0].kwargs["input"] + == "USER JOURNEY: \n[{'timestamp': '1970-01-02T10:17:36Z', 'region': 'Amzing BR', 'artifact': 'Super artifact'}]" + ) + assert ( + call_list[1].kwargs["input"] + == "USER JOURNEY: \n[{'timestamp': '1970-01-02T10:17:36Z', 'region': 'Amzing BR', 'artifact': 'Super artifact'}]" + ) + assert ( + call_list[2].kwargs["input"] + == 'CONVERSATION MESSAGES: \n{"entity": "user", "text": "This is my query."}\n{"entity": "assistant", "text": "sample response content."}' + ) async def streamed_response(): diff --git a/backend/tests/app/routers/test_threads.py b/backend/tests/app/routers/test_threads.py index b3441186a..5faa49d8e 100644 --- a/backend/tests/app/routers/test_threads.py +++ b/backend/tests/app/routers/test_threads.py @@ -75,17 +75,13 @@ def test_generate_thread_title(httpx_mock, app_client, db_connection, test_user_ ).json() assert create_thread_response["title"] == "Great Title" - mock_openai_client.assert_create_called_with_structure_output( + mock_openai_client.assert_responses_parse_called_with( **{ - "messages": [ - { - "role": "system", - "content": "Given the user's first message of a conversation, generate a short title for this conversation (max 5 words).", - }, - {"role": "user", "content": "This is my query"}, - ], + "instructions": "Given the user's first message of a conversation, generate a short title for this conversation (max 5 words).", + "input": "This is my query", "model": "great_model", - "response_format": ThreadGeneratedTitle, + "text_format": ThreadGeneratedTitle, + "store": False, } ) @@ -398,29 +394,21 @@ async def test_get_thread_messages( f"/threads/{thread.thread_id}/messages", params={"sort": "creation_date"} ).json()["results"] + # With new schema: only 2 messages (USER and ASSISTANT) + assert len(messages) == 2 + + # First message: USER assert messages[0]["entity"] == "user" - assert messages[0]["msg_content"] == {"content": "This is my query."} assert messages[0]["message_id"] assert messages[0]["creation_date"] - assert messages[1]["entity"] == "ai_tool" - assert messages[1]["msg_content"] == {"content": ""} + # Second message: ASSISTANT (contains all AI responses as parts) + assert messages[1]["entity"] == "assistant" assert messages[1]["message_id"] assert messages[1]["creation_date"] - assert messages[2]["entity"] == "tool" - assert messages[2]["msg_content"] == {"content": "It's sunny today."} - assert messages[2]["message_id"] - assert messages[2]["creation_date"] - - assert messages[3]["entity"] == "ai_message" - assert messages[3]["msg_content"] == {"content": "sample response content."} - assert messages[3]["message_id"] - assert messages[3]["creation_date"] - + # Verify chronological order assert messages[0]["creation_date"] < messages[1]["creation_date"] - assert messages[1]["creation_date"] < messages[2]["creation_date"] - assert messages[2]["creation_date"] < messages[3]["creation_date"] @pytest.mark.httpx_mock(can_send_already_matched_responses=True) @@ -447,48 +435,29 @@ async def test_get_thread_messages_sort_and_filter( with app_client as app_client: response = app_client.get( f"/threads/{thread.thread_id}/messages", - params={"sort": "-creation_date", "entity": ["USER", "TOOL"]}, + params={"sort": "-creation_date", "entity": ["USER"]}, ) messages = response.json()["results"] - # Expecting only the messages that have the entities "user" and "tool". - # From the populate_db fixture these are: - # - The first message: entity "user", msg_content {"content": "This is my query."} - # - The third message: entity "tool", msg_content {"content": "It's sunny today."} - assert len(messages) == 2 - - # Check that messages are sorted in ascending order by creation_date. - assert messages[0]["creation_date"] >= messages[1]["creation_date"] - - # Verify the filtering: first message should be from "user" and second from "tool" - assert messages[0]["entity"] == "tool" - assert messages[0]["msg_content"] == {"content": "It's sunny today."} - assert messages[1]["entity"] == "user" - assert messages[1]["msg_content"] == {"content": "This is my query."} + # With new schema: only USER entity exists, no TOOL entity + assert len(messages) == 1 + assert messages[0]["entity"] == "user" + assert messages[0]["message_id"] + assert messages[0]["creation_date"] - # Test sorting in descending order (newest first) and filtering for AI_TOOL and AI_MESSAGE messages. + # Test filtering for ASSISTANT messages with app_client as app_client: response = app_client.get( f"/threads/{thread.thread_id}/messages", - params={"sort": "creation_date", "entity": ["AI_TOOL", "AI_MESSAGE"]}, + params={"sort": "creation_date", "entity": ["ASSISTANT"]}, ) messages = response.json()["results"] - # Expecting only the messages that have the entities "ai_tool" and "ai_message". - # According to populate_db these are: - # - The second message: entity "ai_tool", msg_content {"content": ""} - # - The fourth message: entity "ai_message", msg_content {"content": "sample response content."} - assert len(messages) == 2 - - # Check that messages are sorted in descending order by creation_date. - assert messages[0]["creation_date"] <= messages[1]["creation_date"] - - # Verify the filtering: - # Assuming the newer message (by creation_date) is "ai_message" - assert messages[0]["entity"] == "ai_tool" - assert messages[0]["msg_content"] == {"content": ""} - assert messages[1]["entity"] == "ai_message" - assert messages[1]["msg_content"] == {"content": "sample response content."} + # With new schema: only one ASSISTANT message with multiple parts + assert len(messages) == 1 + assert messages[0]["entity"] == "assistant" + assert messages[0]["message_id"] + assert messages[0]["creation_date"] @pytest.mark.httpx_mock(can_send_already_matched_responses=True) @@ -510,44 +479,46 @@ async def test_get_thread_messages_paginated( thread = db_items["thread"] with app_client as app_client: - # Get the messages of the thread + # Get the messages of the thread with page_size=1 messages = app_client.get( - f"/threads/{thread.thread_id}/messages", params={"page_size": 3} + f"/threads/{thread.thread_id}/messages", params={"page_size": 1} ).json() page_2 = app_client.get( f"/threads/{thread.thread_id}/messages", - params={"page_size": 3, "cursor": messages["next_cursor"]}, + params={"page_size": 1, "cursor": messages["next_cursor"]}, ).json() assert set(messages.keys()) == {"next_cursor", "has_more", "page_size", "results"} - assert messages["page_size"] == 3 - assert messages["next_cursor"] == messages["results"][-1]["creation_date"] + # First page: page_size=1, should have 1 message and indicate more available + assert messages["page_size"] == 1 assert messages["has_more"] - assert len(messages["results"]) == 3 + assert len(messages["results"]) == 1 + assert messages["next_cursor"] == messages["results"][-1]["creation_date"] messages_results = messages["results"] - assert messages_results[2]["entity"] == "ai_tool" - assert messages_results[2]["msg_content"] == {"content": ""} - assert messages_results[2]["message_id"] - assert messages_results[2]["creation_date"] - - assert messages_results[1]["entity"] == "tool" - assert messages_results[1]["msg_content"] == {"content": "It's sunny today."} - assert messages_results[1]["message_id"] - assert messages_results[1]["creation_date"] - - assert messages_results[0]["entity"] == "ai_message" - assert messages_results[0]["msg_content"] == {"content": "sample response content."} + # First result (newest): ASSISTANT message + assert messages_results[0]["entity"] == "assistant" assert messages_results[0]["message_id"] assert messages_results[0]["creation_date"] - assert messages_results[0]["creation_date"] > messages_results[1]["creation_date"] - assert messages_results[1]["creation_date"] > messages_results[2]["creation_date"] - + # Second page: should have the remaining USER message + assert set(page_2.keys()) == {"next_cursor", "has_more", "page_size", "results"} + assert page_2["page_size"] == 1 + assert not page_2["has_more"] assert len(page_2["results"]) == 1 + page_2_results = page_2["results"] + + # Second result (oldest): USER message + assert page_2_results[0]["entity"] == "user" + assert page_2_results[0]["message_id"] + assert page_2_results[0]["creation_date"] + + # Verify descending order (newest first) + assert messages_results[0]["creation_date"] > page_2_results[0]["creation_date"] + @pytest.mark.httpx_mock(can_send_already_matched_responses=True) @pytest.mark.asyncio @@ -628,43 +599,33 @@ async def test_get_thread_messages_vercel_format( assert item["id"] assert item["createdAt"] assert item.get("role") == "assistant" - assert item.get("content") == "sample response content." parts = item.get("parts") assert isinstance(parts, list) - assert len(parts) == 3 + assert len(parts) == 2 + # First part: FUNCTION_CALL first_part = parts[0] - assert first_part.get("type") == "text" - assert first_part.get("text") == "" - - second_part = parts[1] - assert second_part.get("type") == "tool-invocation" - tool_inv = second_part.get("toolInvocation") - assert isinstance(tool_inv, dict) - assert tool_inv.get("toolCallId") == "mock_id_tc" - assert tool_inv.get("toolName") == "get_weather" - assert tool_inv.get("args") == {"location": "Geneva"} - assert tool_inv.get("state") == "call" - assert tool_inv.get("results") is None - - third_part = parts[2] + assert isinstance(first_part, dict) + assert first_part.get("toolCallId") == "mock_call_id" + assert first_part.get("type") == "tool-get_weather" + assert first_part.get("input") == {"location": "Geneva"} + assert first_part.get("state") == "output-available" + + # Second part: MESSAGE + third_part = parts[1] assert third_part.get("type") == "text" assert third_part.get("text") == "sample response content." - annotations = item.get("annotations") - assert isinstance(annotations, list) - assert len(annotations) == 2 + metadata = item.get("metadata").get("toolCalls") + assert isinstance(metadata, list) + assert len(metadata) == 1 - ann1 = annotations[0] - assert ann1.get("toolCallId") == "mock_id_tc" + ann1 = metadata[0] + assert ann1.get("toolCallId") == "mock_call_id" assert ann1.get("validated") == "not_required" assert ann1.get("isComplete") is True - ann2 = annotations[1] - assert ann2["messageId"] - assert ann2.get("isComplete") is True - # Assert the second page assert len(page_2["results"]) == 1 assert page_2["has_more"] is False @@ -672,9 +633,8 @@ async def test_get_thread_messages_vercel_format( assert page_2["page_size"] == 2 msg = page_2["results"][0] - assert msg["annotations"] is None - assert msg["content"] == "This is my query." - assert msg["parts"] is None + assert msg["metadata"] is None + assert msg["parts"][0]["text"] == "This is my query." assert msg["role"] == "user" diff --git a/backend/tests/app/routers/test_tools.py b/backend/tests/app/routers/test_tools.py index 64a3c4671..07481610e 100644 --- a/backend/tests/app/routers/test_tools.py +++ b/backend/tests/app/routers/test_tools.py @@ -3,7 +3,6 @@ import pytest from neuroagent.app.config import Settings -from neuroagent.app.database.sql_schemas import Entity from neuroagent.app.dependencies import get_settings, get_tool_list from neuroagent.app.main import app from tests.conftest import mock_keycloak_user_identification @@ -26,20 +25,24 @@ async def test_execute_tool_call_accepted( ) app.dependency_overrides[get_settings] = lambda: test_settings db_items, session = populate_db - thread, _, tool_call = db_items.values() + thread = db_items["thread"] + assistant_message = db_items["assistant_message"] + # Get the FUNCTION_CALL part from assistant message + await session.refresh(assistant_message, ["parts"]) + tool_call_part = assistant_message.parts[0] + tool_id = tool_call_part.output["call_id"] + app.dependency_overrides[get_tool_list] = lambda: [get_weather_tool] with app_client as app_client: response = app_client.patch( - f"/tools/{thread.thread_id}/execute/{tool_call.tool_call_id}", + f"/tools/{thread.thread_id}/execute/{tool_id}", json={"validation": "accepted"}, ) assert response.json()["status"] == "done" - # Check if validation status changed and new tool message appeared - await session.refresh(tool_call) - assert tool_call.validated - messages = await thread.awaitable_attrs.messages - assert messages[-1].entity == Entity.TOOL + # Check if validation status changed + await session.refresh(tool_call_part) + assert tool_call_part.validated @pytest.mark.asyncio @@ -59,58 +62,25 @@ async def test_execute_tool_call_rejected( ) app.dependency_overrides[get_settings] = lambda: test_settings db_items, session = populate_db - thread, _, tool_call = db_items.values() + thread = db_items["thread"] + assistant_message = db_items["assistant_message"] + # Get the FUNCTION_CALL part from assistant message + await session.refresh(assistant_message, ["parts"]) + tool_call_part = assistant_message.parts[0] + tool_id = tool_call_part.output["call_id"] app.dependency_overrides[get_tool_list] = lambda: [get_weather_tool] with app_client as app_client: response = app_client.patch( - f"/tools/{thread.thread_id}/execute/{tool_call.tool_call_id}", + f"/tools/{thread.thread_id}/execute/{tool_id}", json={"validation": "rejected"}, ) assert response.json()["status"] == "done" - # Check if validation status changed and new tool message appeared - await session.refresh(tool_call) - assert not tool_call.validated - messages = await thread.awaitable_attrs.messages - assert messages[-1].entity == Entity.TOOL - - -@pytest.mark.asyncio -async def test_execute_tool_call_validation_error( - httpx_mock, - app_client, - db_connection, - populate_db, - get_weather_tool, - test_user_info, -): - mock_keycloak_user_identification(httpx_mock, test_user_info) - test_settings = Settings( - db={"prefix": db_connection}, - keycloak={"issuer": "https://great_issuer.com"}, - llm={"base_url": "http://cool.com", "open_router_token": "sk-or-cool"}, - ) - app.dependency_overrides[get_settings] = lambda: test_settings - db_items, session = populate_db - thread, _, tool_call = db_items.values() - - app.dependency_overrides[get_tool_list] = lambda: [get_weather_tool] - - with app_client as app_client: - response = app_client.patch( - f"/tools/{thread.thread_id}/execute/{tool_call.tool_call_id}", - json={ - "validation": "accepted", - "args": json.dumps({"lction": "Zurich"}), - }, # Make a mistake in the args json - ) - - # Check if validation status didn't change - assert response.json()["status"] == "validation-error" - await session.refresh(tool_call) - assert tool_call.validated is None + # Check if validation status changed + await session.refresh(tool_call_part) + assert not tool_call_part.validated @pytest.mark.asyncio diff --git a/backend/tests/app/test_app_utils.py b/backend/tests/app/test_app_utils.py index 8ed1d15d6..cde0f1d44 100644 --- a/backend/tests/app/test_app_utils.py +++ b/backend/tests/app/test_app_utils.py @@ -1,6 +1,5 @@ """Test app utils.""" -import json from datetime import datetime, timezone from typing import Literal from unittest.mock import AsyncMock, patch @@ -20,18 +19,13 @@ validate_project, ) from neuroagent.app.config import Settings -from neuroagent.app.database.sql_schemas import Entity, Messages, ToolCalls +from neuroagent.app.database.sql_schemas import Entity, Messages, Parts, PartType from neuroagent.app.schemas import ( - AnnotationMessageVercel, - AnnotationToolCallVercel, - MessagesRead, - MessagesReadVercel, PaginatedResponse, RateLimitInfo, + ReasoningPartVercel, TextPartVercel, - ToolCall, ToolCallPartVercel, - ToolCallVercel, UserInfo, ) from tests.mock_client import MockOpenAIClient, create_mock_response @@ -236,208 +230,157 @@ async def test_rate_limit_no_redis(): def test_format_messages_output(): - """Test the output format conversion.""" + """Test format_messages_output with multiple messages and parts.""" msg1 = Messages( - creation_date=datetime(2025, 6, 4, 14, 4, 41, tzinfo=timezone.utc), - entity=Entity.AI_MESSAGE, - is_complete=True, - message_id="359eeb21-2e94-4095-94d9-ca7d4ff22640", - content=json.dumps({"content": "DUMMY_AI_CONTENT"}), - thread_id="e2db8c7d-1170-4762-b42b-fdcd08526735", - tool_calls=[], - ) - msg2 = Messages( - creation_date=datetime(2025, 6, 4, 14, 4, 41, tzinfo=timezone.utc), - entity=Entity.TOOL, - is_complete=True, - message_id="06c305de-1562-43aa-adea-beeeb53880a2", - content=json.dumps({"content": "DUMMY_RESULT"}), - thread_id="e2db8c7d-1170-4762-b42b-fdcd08526735", - tool_calls=[], - ) - dummy_tool_call = ToolCalls( - tool_call_id="1234", - arguments="{}", - name="dummy_tool", - validated="not_required", - ) - msg3 = Messages( - creation_date=datetime(2025, 6, 4, 14, 4, 41, tzinfo=timezone.utc), - entity=Entity.AI_TOOL, - is_complete=True, - message_id="e21d5f16-8553-4181-9d25-d1d935327ffc", - content=json.dumps({"content": "DUMMY_AI_TOOL_CONTENT"}), - thread_id="e2db8c7d-1170-4762-b42b-fdcd08526735", - tool_calls=[dummy_tool_call], - ) - msg4 = Messages( - creation_date=datetime(2025, 6, 4, 14, 4, 41, tzinfo=timezone.utc), + message_id=UUID("359eeb21-2e94-4095-94d9-ca7d4ff22640"), entity=Entity.USER, - is_complete=True, - message_id="87866e27-dc78-48c2-bd68-4ea395d5a466", - content=json.dumps({"content": "DUMMY_USER_TEXT"}), - thread_id="e2db8c7d-1170-4762-b42b-fdcd08526735", - tool_calls=[], - ) - - fake_message_list = [msg1, msg2, msg3, msg4] - - expected_output = PaginatedResponse( - next_cursor=datetime(2025, 6, 4, 14, 4, 41, tzinfo=timezone.utc), - has_more=False, - page_size=10, - results=[ - MessagesRead( - message_id="359eeb21-2e94-4095-94d9-ca7d4ff22640", - entity="ai_message", - thread_id="e2db8c7d-1170-4762-b42b-fdcd08526735", - is_complete=True, - creation_date=datetime(2025, 6, 4, 14, 4, 41, tzinfo=timezone.utc), - msg_content={"content": "DUMMY_AI_CONTENT"}, - tool_calls=[], - ), - MessagesRead( - message_id="06c305de-1562-43aa-adea-beeeb53880a2", - entity="tool", - thread_id="e2db8c7d-1170-4762-b42b-fdcd08526735", - is_complete=True, - creation_date=datetime(2025, 6, 4, 14, 4, 41, tzinfo=timezone.utc), - msg_content={"content": "DUMMY_RESULT"}, - tool_calls=[], - ), - MessagesRead( - message_id="e21d5f16-8553-4181-9d25-d1d935327ffc", - entity="ai_tool", - thread_id="e2db8c7d-1170-4762-b42b-fdcd08526735", - is_complete=True, - creation_date=datetime(2025, 6, 4, 14, 4, 41, tzinfo=timezone.utc), - msg_content={"content": "DUMMY_AI_TOOL_CONTENT"}, - tool_calls=[ - ToolCall( - tool_call_id="1234", - name="dummy_tool", - arguments="{}", - validated="not_required", - ) - ], - ), - MessagesRead( - message_id="87866e27-dc78-48c2-bd68-4ea395d5a466", - entity="user", - thread_id="e2db8c7d-1170-4762-b42b-fdcd08526735", - is_complete=True, - creation_date=datetime(2025, 6, 4, 14, 4, 41, tzinfo=timezone.utc), - msg_content={"content": "DUMMY_USER_TEXT"}, - tool_calls=[], - ), - ], + thread_id=UUID("e2db8c7d-1170-4762-b42b-fdcd08526735"), + creation_date=datetime(2025, 6, 4, 14, 4, 41, tzinfo=timezone.utc), ) + msg1.parts = [ + Parts( + order_index=0, + type=PartType.MESSAGE, + output={"content": [{"type": "text", "text": "User message"}]}, + is_complete=True, + ) + ] - fake_formated_response = format_messages_output( - fake_message_list, {"dummy_tool": False}, False, 10 + msg2 = Messages( + message_id=UUID("459eeb21-2e94-4095-94d9-ca7d4ff22641"), + entity=Entity.ASSISTANT, + thread_id=UUID("e2db8c7d-1170-4762-b42b-fdcd08526735"), + creation_date=datetime(2025, 6, 4, 14, 5, 0, tzinfo=timezone.utc), ) + msg2.parts = [ + Parts( + order_index=0, + type=PartType.MESSAGE, + output={ + "content": [ + {"type": "text", "text": "Response 1"}, + {"type": "text", "text": "Response 2"}, + ] + }, + is_complete=True, + ), + Parts( + order_index=1, + type=PartType.MESSAGE, + output={"content": []}, + is_complete=True, + ), + ] - assert fake_formated_response == expected_output + result = format_messages_output([msg1, msg2], True, 5) + + assert isinstance(result, PaginatedResponse) + assert result.has_more is True + assert result.page_size == 5 + assert result.next_cursor == msg2.creation_date + assert len(result.results) == 2 + assert result.results[0].entity == "user" + assert result.results[0].parts == [{"type": "text", "text": "User message"}] + assert result.results[1].entity == "assistant" + assert result.results[1].parts == [ + {"type": "text", "text": "Response 1"}, + {"type": "text", "text": "Response 2"}, + ] def test_format_messages_vercel(): - """Test the output format conversion to vercel.""" + """Test format_messages_vercel with all part types and validation states.""" msg1 = Messages( - creation_date=datetime(2025, 6, 4, 14, 4, 41, tzinfo=timezone.utc), - entity=Entity.AI_MESSAGE, - is_complete=True, - message_id="359eeb212e94409594d9ca7d4ff22640", - content=json.dumps({"content": "DUMMY_AI_CONTENT"}), - thread_id="e2db8c7d11704762b42bfdcd08526735", - tool_calls=[], - ) - msg2 = Messages( - creation_date=datetime(2025, 6, 4, 14, 4, 41, tzinfo=timezone.utc), - entity=Entity.TOOL, - is_complete=True, - message_id="06c305de156243aaadeabeeeb53880a2", - content=json.dumps({"content": "DUMMY_RESULT"}), - thread_id="e2db8c7d11704762b42bfdcd08526735", - tool_calls=[], - ) - dummy_tool_call = ToolCalls( - tool_call_id="1234", - arguments="{}", - name="dummy_tool", - validated="not_required", - ) - msg3 = Messages( - creation_date=datetime(2025, 6, 4, 14, 4, 41, tzinfo=timezone.utc), - entity=Entity.AI_TOOL, - is_complete=True, - message_id="e21d5f16855341819d25d1d935327ffc", - content=json.dumps({"content": "DUMMY_AI_TOOL_CONTENT"}), - thread_id="e2db8c7d11704762b42bfdcd08526735", - tool_calls=[dummy_tool_call], - ) - msg4 = Messages( - creation_date=datetime(2025, 6, 4, 14, 4, 41, tzinfo=timezone.utc), + message_id=UUID("359eeb21-2e94-4095-94d9-ca7d4ff22640"), entity=Entity.USER, - is_complete=True, - message_id="87866e27dc7848c2bd684ea395d5a466", - content=json.dumps({"content": "DUMMY_USER_TEXT"}), - thread_id="e2db8c7d11704762b42bfdcd08526735", - tool_calls=[], + thread_id=UUID("e2db8c7d-1170-4762-b42b-fdcd08526735"), + creation_date=datetime(2025, 6, 4, 14, 4, 41, tzinfo=timezone.utc), ) + msg1.parts = [ + Parts( + order_index=0, + type=PartType.MESSAGE, + output={"content": [{"text": "User question"}]}, + is_complete=True, + ) + ] - fake_message_list = [msg1, msg2, msg3, msg4] - - expected_output = PaginatedResponse( - next_cursor=None, - has_more=False, - page_size=10, - results=[ - MessagesReadVercel( - id="359eeb212e94409594d9ca7d4ff22640", - role="assistant", - createdAt=datetime(2025, 6, 4, 14, 4, 41, tzinfo=timezone.utc), - content="DUMMY_AI_CONTENT", - parts=[ - TextPartVercel(type="text", text="DUMMY_AI_TOOL_CONTENT"), - ToolCallPartVercel( - type="tool-invocation", - toolInvocation=ToolCallVercel( - toolCallId="1234", - toolName="dummy_tool", - args={}, - state="call", - results=None, - ), - ), - TextPartVercel(type="text", text="DUMMY_AI_CONTENT"), - ], - annotations=[ - AnnotationToolCallVercel( - toolCallId="1234", validated="not_required", isComplete=True - ), - AnnotationMessageVercel( - messageId="359eeb212e94409594d9ca7d4ff22640", isComplete=True - ), - ], - ), - MessagesReadVercel( - id="87866e27dc7848c2bd684ea395d5a466", - role="user", - createdAt=datetime(2025, 6, 4, 14, 4, 41, tzinfo=timezone.utc), - content="DUMMY_USER_TEXT", - parts=None, - annotations=None, - ), - ], + msg2 = Messages( + message_id=UUID("459eeb21-2e94-4095-94d9-ca7d4ff22641"), + entity=Entity.ASSISTANT, + thread_id=UUID("e2db8c7d-1170-4762-b42b-fdcd08526735"), + creation_date=datetime(2025, 6, 4, 14, 5, 0, tzinfo=timezone.utc), ) + msg2.parts = [ + Parts( + order_index=0, + type=PartType.REASONING, + output={"summary": [{"text": "Thinking"}, {"text": "Analyzing"}]}, + is_complete=True, + ), + Parts( + order_index=1, + type=PartType.FUNCTION_CALL, + output={"call_id": "call_1", "name": "tool_no_hil", "arguments": "{}"}, + is_complete=True, + validated=None, + ), + Parts( + order_index=2, + type=PartType.FUNCTION_CALL_OUTPUT, + output={"call_id": "call_1", "output": "Result"}, + is_complete=True, + ), + Parts( + order_index=3, + type=PartType.FUNCTION_CALL, + output={"call_id": "call_2", "name": "tool_hil", "arguments": "{}"}, + is_complete=False, + validated=True, + ), + Parts( + order_index=4, + type=PartType.MESSAGE, + output={"content": [{"text": "Final answer"}]}, + is_complete=True, + ), + ] - fake_formated_response_vercel = format_messages_vercel( - fake_message_list, {"dummy_tool": False}, False, 10 + result = format_messages_vercel( + [msg1, msg2], {"tool_no_hil": False, "tool_hil": True}, True, 5 ) - assert fake_formated_response_vercel == expected_output + assert isinstance(result, PaginatedResponse) + assert result.has_more is True + assert result.page_size == 5 + assert result.next_cursor == msg2.creation_date + assert len(result.results) == 2 + + assert result.results[0].role == "user" + assert result.results[0].metadata is None + assert len(result.results[0].parts) == 1 + assert isinstance(result.results[0].parts[0], TextPartVercel) + + assert result.results[1].role == "assistant" + assert len(result.results[1].parts) == 5 + assert isinstance(result.results[1].parts[0], ReasoningPartVercel) + assert result.results[1].parts[0].text == "Thinking" + assert isinstance(result.results[1].parts[1], ReasoningPartVercel) + assert isinstance(result.results[1].parts[2], ToolCallPartVercel) + assert result.results[1].parts[2].state == "output-available" + assert result.results[1].parts[2].output == "Result" + assert isinstance(result.results[1].parts[3], ToolCallPartVercel) + assert result.results[1].parts[3].state == "input-available" + assert isinstance(result.results[1].parts[4], TextPartVercel) + + assert len(result.results[1].metadata.toolCalls) == 2 + assert result.results[1].metadata.toolCalls[0].validated == "not_required" + assert result.results[1].metadata.toolCalls[0].isComplete is True + assert result.results[1].metadata.toolCalls[1].validated == "accepted" + assert result.results[1].metadata.toolCalls[1].isComplete is True + assert result.results[1].isComplete is False @pytest.fixture() @@ -573,14 +516,24 @@ def test_various_limit_values(sample_redis_info, limit_value): async def test_filter_tools_empty_tool_list(): """Test that empty tool list returns empty list""" settings = Settings() - messages = [ - Messages( - entity=Entity.USER, - content=json.dumps({"role": "user", "content": "Hello"}), - thread_id=UUID("12345678-9123-4567-1234-890123456789"), + user_message = Messages( + entity=Entity.USER, + thread_id=UUID("12345678-9123-4567-1234-890123456789"), + ) + user_message.parts = [ + Parts( + order_index=0, + type=PartType.MESSAGE, + output={ + "content": [{"text": "Hello", "type": "input_text"}], + "role": "user", + "status": "completed", + "type": "message", + }, is_complete=True, ) ] + messages = [user_message] result, model_dict = await filter_tools_and_model_by_conversation( messages=messages, tool_list=[], @@ -594,7 +547,6 @@ async def test_filter_tools_empty_tool_list(): @pytest.mark.asyncio async def test_filter_tools_successful_selection(get_weather_tool, agent_handoff_tool): """Test successful tool filtering""" - # Mock OpenAI response mock_openai_client = MockOpenAIClient() class ToolFiltering(BaseModel): @@ -618,29 +570,61 @@ class ToolFiltering(BaseModel): ), ) ) - messages = [ - Messages( - entity=Entity.USER, - content=json.dumps({"role": "user", "content": "Hello"}), - thread_id=UUID("12345678-9123-4567-1234-890123456789"), + msg1 = Messages( + entity=Entity.USER, thread_id=UUID("12345678-9123-4567-1234-890123456789") + ) + msg1.parts = [ + Parts( + order_index=0, + type=PartType.MESSAGE, + output={ + "content": [{"text": "Hello", "type": "input_text"}], + "role": "user", + "status": "completed", + "type": "message", + }, is_complete=True, - ), - Messages( - entity=Entity.AI_MESSAGE, - content=json.dumps({"role": "assistant", "content": "Hi there!"}), - thread_id=UUID("12345678-9123-4567-1234-890123456789"), + ) + ] + + msg2 = Messages( + entity=Entity.ASSISTANT, thread_id=UUID("12345678-9123-4567-1234-890123456789") + ) + msg2.parts = [ + Parts( + order_index=0, + type=PartType.MESSAGE, + output={ + "content": [{"text": "Hi there!"}], + "role": "assistant", + "status": "completed", + "type": "message", + }, is_complete=True, - ), - Messages( - entity=Entity.USER, - content=json.dumps( - {"role": "user", "content": "I need help with Agent handoff"} - ), - thread_id=UUID("12345678-9123-4567-1234-890123456789"), + ) + ] + + msg3 = Messages( + entity=Entity.USER, thread_id=UUID("12345678-9123-4567-1234-890123456789") + ) + msg3.parts = [ + Parts( + order_index=0, + type=PartType.MESSAGE, + output={ + "content": [ + {"text": "I need help with Agent handoff", "type": "input_text"} + ], + "role": "user", + "status": "completed", + "type": "message", + }, is_complete=True, - ), + ) ] + messages = [msg1, msg2, msg3] + settings = Settings(tools={"min_tool_selection": 1}) with patch( "neuroagent.app.app_utils.get_token_count", @@ -680,14 +664,23 @@ class ToolFiltering(BaseModel): structured_output_class=ToolFiltering(selected_tools=["get_weather"]), ) ) - messages = [ - Messages( - entity=Entity.USER, - content=json.dumps({"role": "user", "content": "What's the weather?"}), - thread_id=UUID("12345678-9123-4567-1234-890123456789"), + user_message = Messages( + entity=Entity.USER, thread_id=UUID("12345678-9123-4567-1234-890123456789") + ) + user_message.parts = [ + Parts( + order_index=0, + type=PartType.MESSAGE, + output={ + "content": [{"text": "What's the weather?", "type": "input_text"}], + "role": "user", + "status": "completed", + "type": "message", + }, is_complete=True, ) ] + messages = [user_message] settings = Settings(tools={"min_tool_selection": 1}) with patch( @@ -715,14 +708,23 @@ class ToolFiltering(BaseModel): @pytest.mark.asyncio async def test_filter_tools_no_selection_needed(get_weather_tool): """Test when neither tool nor model selection is needed""" - messages = [ - Messages( - entity=Entity.USER, - content=json.dumps({"role": "user", "content": "Hello"}), - thread_id=UUID("12345678-9123-4567-1234-890123456789"), + user_message = Messages( + entity=Entity.USER, thread_id=UUID("12345678-9123-4567-1234-890123456789") + ) + user_message.parts = [ + Parts( + order_index=0, + type=PartType.MESSAGE, + output={ + "content": [{"text": "Hello", "type": "input_text"}], + "role": "user", + "status": "completed", + "type": "message", + }, is_complete=True, ) ] + messages = [user_message] settings = Settings(tools={"min_tool_selection": 5}) result, model_dict = await filter_tools_and_model_by_conversation( @@ -757,14 +759,24 @@ class ComplexityFiltering(BaseModel): structured_output_class=ComplexityFiltering(complexity=7), ) ) - messages = [ - Messages( - entity=Entity.USER, - content=json.dumps({"role": "user", "content": "Complex query"}), - thread_id=UUID("12345678-9123-4567-1234-890123456789"), + user_message = Messages( + entity=Entity.USER, + thread_id=UUID("12345678-9123-4567-1234-890123456789"), + ) + user_message.parts = [ + Parts( + order_index=0, + type=PartType.MESSAGE, + output={ + "content": [{"text": "Complex query", "type": "input_text"}], + "role": "user", + "status": "completed", + "type": "message", + }, is_complete=True, ) ] + messages = [user_message] settings = Settings() with patch( @@ -784,5 +796,5 @@ class ComplexityFiltering(BaseModel): assert len(result) == 1 assert result[0].name == "get_weather" - assert model_dict["model"] == "openai/gpt-5-mini" - assert model_dict["reasoning"] == "medium" + assert model_dict["model"] == "gpt-5-mini" + assert model_dict["reasoning"] == "low" diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index 5a9429bc6..6fbd80078 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -1,6 +1,5 @@ """Test configuration.""" -import json import os from typing import ClassVar from unittest.mock import AsyncMock, mock_open, patch @@ -17,8 +16,9 @@ from neuroagent.app.database.sql_schemas import ( Entity, Messages, + Parts, + PartType, Threads, - ToolCalls, ) from neuroagent.app.dependencies import ( Agent, @@ -122,7 +122,7 @@ class FakeTool(BaseTool): description_frontend: ClassVar[str] = "Great description frontend" metadata: FakeToolMetadata input_schema: FakeToolInput - hil: ClassVar[bool] = True + hil: ClassVar[bool] = False async def arun(self) -> FakeToolOutput: if self.metadata.planet: @@ -249,7 +249,7 @@ async def setup_sql_db(request): async with engine.begin() as conn: await conn.run_sync(metadata.reflect) tables = metadata.tables - await session.execute(tables["tool_calls"].delete()) + await session.execute(tables["parts"].delete()) await session.execute(tables["messages"].delete()) await session.execute(tables["threads"].delete()) @@ -271,53 +271,76 @@ async def populate_db(db_connection, test_user_info): title="Test Thread", ) - # Create four dummy messages associated with the thread - messages = [ - Messages( - entity=Entity.USER, - content=json.dumps({"content": "This is my query."}), - thread=thread, + user_message = Messages(entity=Entity.USER, thread=thread) + user_message.parts = [ + Parts( + order_index=0, + type=PartType.MESSAGE, + output={ + "content": [{"text": "This is my query.", "type": "input_text"}], + "role": "user", + "status": "completed", + "type": "message", + }, is_complete=True, - ), - Messages( - entity=Entity.AI_TOOL, - content=json.dumps({"content": ""}), - thread=thread, + ) + ] + + assistant_message = Messages(entity=Entity.ASSISTANT, thread=thread) + assistant_message.parts = [ + Parts( + order_index=0, + type=PartType.FUNCTION_CALL, + output={ + "id": "mock_tc_id", + "call_id": "mock_call_id", + "name": "get_weather", + "arguments": '{"location": "Geneva"}', + "status": "completed", + "type": "function_call", + }, is_complete=True, + validated=None, ), - Messages( - entity=Entity.TOOL, - content=json.dumps({"content": "It's sunny today."}), - thread=thread, + Parts( + order_index=1, + type=PartType.FUNCTION_CALL_OUTPUT, + output={ + "id": "mock_tc_id", + "call_id": "mock_call_id", + "output": "It's sunny today.", + "status": "completed", + "type": "function_call_output", + }, is_complete=True, ), - Messages( - entity=Entity.AI_MESSAGE, - content=json.dumps({"content": "sample response content."}), - thread=thread, + Parts( + order_index=2, + type=PartType.MESSAGE, + output={ + "content": [{"text": "sample response content."}], + "role": "assistant", + "status": "completed", + "type": "message", + }, is_complete=True, ), ] - tool_call = ToolCalls( - tool_call_id="mock_id_tc", - name="get_weather", - arguments=json.dumps({"location": "Geneva"}), - validated=None, - message=messages[1], - ) - - # Add them to the session - session.add(thread) - session.add_all(messages) - session.add(tool_call) + session.add_all([thread, user_message, assistant_message]) - # Commit the transaction to persist them in the test database await session.commit() await session.refresh(thread) - await session.refresh(tool_call) - # Return the created objects so they can be used in tests - yield {"thread": thread, "messages": messages, "tool_call": tool_call}, session + await session.refresh(user_message) + await session.refresh(assistant_message) + yield ( + { + "thread": thread, + "user_message": user_message, + "assistant_message": assistant_message, + }, + session, + ) await session.close() diff --git a/backend/tests/mock_client.py b/backend/tests/mock_client.py index a07bbedec..19ed79557 100644 --- a/backend/tests/mock_client.py +++ b/backend/tests/mock_client.py @@ -2,11 +2,7 @@ from unittest.mock import AsyncMock, Mock from openai import AsyncOpenAI -from openai.types.chat.chat_completion import ChatCompletion -from openai.types.chat.chat_completion_message_tool_call import ( - ChatCompletionMessageToolCall, - Function, -) +from openai.types.responses import ResponseFunctionToolCall def create_mock_response( @@ -17,72 +13,59 @@ def create_mock_response( ): role = message.get("role", "assistant") content = message.get("content", "") - tool_calls = ( - [ - ChatCompletionMessageToolCall( - id="mock_tc_id", - type="function", - function=Function( - name=call.get("name", ""), - arguments=json.dumps(call.get("args", {})), - ), - ) - for call in function_calls - ] - if function_calls - else None - ) - return Mock( - id="mock_cc_id", - created=1234567890, - model=model, - object="chat.completion", - choices=[ - Mock( - message=Mock( - role=role, - content=content, - tool_calls=tool_calls, - parsed=structured_output_class, - ), - finish_reason="stop", - index=0, + + output = [] + + if content: + output.append( + { + "id": "msg_mock_id", + "type": "message", + "status": "completed", + "role": role, + "content": [{"type": "output_text", "text": content}], + } + ) + + if function_calls is not None: + for function_call in function_calls: + output.append( + ResponseFunctionToolCall( + **{ + "id": function_call.get("id", "fc_mock_id"), + "type": "function_call", + "status": "completed", + "name": function_call.get("name"), + "call_id": function_call.get("call_id", "fc_mock_call_id"), + "arguments": json.dumps(function_call.get("args", {})), + } + ) ) - ], - ) + + mock_resp = Mock() + mock_resp.id = "resp_mock_id" + mock_resp.model = model + mock_resp.output = output + mock_resp.output_parsed = structured_output_class + return mock_resp class MockOpenAIClient: def __init__(self): - self.chat = AsyncMock() - self.chat.completions = AsyncMock() - self.beta = AsyncMock() - self.beta.chat = AsyncMock() - self.beta.chat.completions = AsyncMock() + self.responses = AsyncMock() + self.responses.create = AsyncMock() + self.responses.parse = AsyncMock() @property def __class__(self): - # pretend to be the real AsyncOpenAI return AsyncOpenAI - def set_response(self, response: ChatCompletion): - """ - Set the mock to return a specific response. - :param response: A ChatCompletion response to return. - """ - self.chat.completions.create.return_value = response - self.beta.chat.completions.parse.return_value = response - - def set_sequential_responses(self, responses: list[ChatCompletion]): - """ - Set the mock to return different responses sequentially. - :param responses: A list of ChatCompletion responses to return in order. - """ - self.chat.completions.create.side_effect = responses - self.beta.chat.completions.parse.side_effect = responses + def set_response(self, response): + self.responses.create.return_value = response + self.responses.parse.return_value = response - def assert_create_called_with(self, **kwargs): - self.chat.completions.create.assert_called_with(**kwargs) + def assert_responses_create_called_with(self, **kwargs): + self.responses.create.assert_called_with(**kwargs) - def assert_create_called_with_structure_output(self, **kwargs): - self.beta.chat.completions.parse.assert_called_with(**kwargs) + def assert_responses_parse_called_with(self, **kwargs): + self.responses.parse.assert_called_with(**kwargs) diff --git a/backend/tests/test_agent_routine.py b/backend/tests/test_agent_routine.py index da3e9ef32..785693d3c 100644 --- a/backend/tests/test_agent_routine.py +++ b/backend/tests/test_agent_routine.py @@ -1,19 +1,31 @@ import json -from typing import AsyncIterator from unittest.mock import patch import pytest -from openai.types.chat.chat_completion_chunk import ( - ChatCompletionChunk, - Choice, - ChoiceDelta, - ChoiceDeltaToolCall, - ChoiceDeltaToolCallFunction, +from openai.types.responses import ( + FunctionTool, + ResponseCompletedEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionToolCall, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseOutputMessage, + ResponseOutputText, + ResponseReasoningSummaryPartAddedEvent, + ResponseReasoningSummaryPartDoneEvent, + ResponseReasoningSummaryTextDeltaEvent, + ResponseTextDeltaEvent, + ResponseUsage, +) +from openai.types.responses import ( + Response as OpenAIResponse, ) from pydantic import BaseModel from neuroagent.agent_routine import AgentsRoutine -from neuroagent.app.database.sql_schemas import Entity, Messages, ToolCalls +from neuroagent.app.database.sql_schemas import Entity, Messages, Parts, PartType from neuroagent.new_types import Agent, Response, Result from tests.mock_client import create_mock_response @@ -30,24 +42,25 @@ async def test_get_chat_completion_simple_message(self, mock_openai_client): context_variables={}, model_override=None, ) - mock_openai_client.assert_create_called_with( + + mock_openai_client.assert_responses_create_called_with( **{ + "instructions": "You are a helpful agent.", + "input": [{"role": "user", "content": "Hello !"}], "model": "openai/gpt-5-mini", - "messages": [ - {"role": "system", "content": "You are a helpful agent."}, - {"role": "user", "content": "Hello !"}, - ], - "tools": None, - "tool_choice": None, "stream": False, "temperature": 0, - "seed": 12008, - "reasoning_effort": "minimal", + "tools": [], + "include": ["reasoning.encrypted_content"], + "store": False, + "text": {"verbosity": "medium"}, + "reasoning": {"effort": "minimal", "summary": "auto"}, } ) - - assert response.choices[0].message.role == "assistant" - assert response.choices[0].message.content == "sample response content" + assert response.output[0]["role"] == "assistant" + assert response.output[0]["content"] == [ + {"type": "output_text", "text": "sample response content"} + ] @pytest.mark.asyncio async def test_get_chat_completion_callable_sys_prompt(self, mock_openai_client): @@ -65,27 +78,24 @@ def agent_instruction(context_variables): context_variables={"mrt": "Great mrt", "twng": "Bad twng"}, model_override=None, ) - mock_openai_client.assert_create_called_with( + mock_openai_client.assert_responses_create_called_with( **{ + "instructions": "This is your new instructions with Bad twng and Great mrt.", + "input": [{"role": "user", "content": "Hello !"}], "model": "openai/gpt-5-mini", - "messages": [ - { - "role": "system", - "content": "This is your new instructions with Bad twng and Great mrt.", - }, - {"role": "user", "content": "Hello !"}, - ], - "tools": None, - "tool_choice": None, "stream": False, "temperature": 0, - "seed": 12008, - "reasoning_effort": "minimal", + "tools": [], + "include": ["reasoning.encrypted_content"], + "store": False, + "text": {"verbosity": "medium"}, + "reasoning": {"effort": "minimal", "summary": "auto"}, } ) - - assert response.choices[0].message.role == "assistant" - assert response.choices[0].message.content == "sample response content" + assert response.output[0]["role"] == "assistant" + assert response.output[0]["content"] == [ + {"type": "output_text", "text": "sample response content"} + ] @pytest.mark.asyncio async def test_get_chat_completion_tools( @@ -100,49 +110,45 @@ async def test_get_chat_completion_tools( context_variables={}, model_override=None, ) - mock_openai_client.assert_create_called_with( + mock_openai_client.assert_responses_create_called_with( **{ + "instructions": "You are a helpful agent.", + "input": [{"role": "user", "content": "Hello !"}], "model": "openai/gpt-5-mini", - "messages": [ - {"role": "system", "content": "You are a helpful agent."}, - {"role": "user", "content": "Hello !"}, - ], + "stream": False, + "temperature": 0, "tools": [ { "type": "function", "name": "get_weather", "description": "Great description", - "function": { - "name": "get_weather", - "description": "Great description", - "strict": False, - "parameters": { - "properties": { - "location": { - "description": "The location to get the weather for", - "title": "Location", - "type": "string", - } - }, - "required": ["location"], - "title": "FakeToolInput", - "type": "object", - "additionalProperties": False, + "parameters": { + "properties": { + "location": { + "description": "The location to get the weather for", + "title": "Location", + "type": "string", + } }, + "required": ["location"], + "title": "FakeToolInput", + "type": "object", + "additionalProperties": False, }, } ], - "tool_choice": None, - "stream": False, + "include": ["reasoning.encrypted_content"], + "store": False, + "text": {"verbosity": "medium"}, + "reasoning": {"effort": "minimal", "summary": "auto"}, "parallel_tool_calls": True, - "temperature": 0, - "seed": 12008, - "reasoning_effort": "minimal", } ) - assert response.choices[0].message.role == "assistant" - assert response.choices[0].message.content == "sample response content" + assert response.output[0]["role"] == "assistant" + assert response.output[0]["content"] == [ + {"type": "output_text", "text": "sample response content"} + ] def test_handle_function_result(self, mock_openai_client): routine = AgentsRoutine(client=mock_openai_client) @@ -203,29 +209,19 @@ async def test_execute_tool_calls_simple( context_variables=context_variables, model_override=None, ) - tool_calls = tool_call_message.choices[0].message.tool_calls - tool_calls_db = [ - ToolCalls( - tool_call_id=tool_call.id, - name=tool_call.function.name, - arguments=tool_call.function.arguments, - ) - for tool_call in tool_calls - ] + tool_calls = tool_call_message.output tool_calls_result = await routine.execute_tool_calls( - tool_calls=tool_calls_db, + tool_calls=tool_calls, tools=agent.tools, context_variables=context_variables, ) assert isinstance(tool_calls_result, Response) - assert tool_calls_result.messages == [ - { - "role": "tool", - "tool_call_id": tool_calls[0].id, - "tool_name": "get_weather", - "content": '{"output":{"param":"It\'s sunny today."}}', - } - ] + assert len(tool_calls_result.messages) == 1 + assert tool_calls_result.messages[0].call_id == tool_calls[0].call_id + assert ( + tool_calls_result.messages[0].output + == '{"output":{"param":"It\'s sunny today."}}' + ) assert tool_calls_result.agent is None assert tool_calls_result.context_variables == context_variables @@ -253,36 +249,21 @@ async def test_execute_multiple_tool_calls( context_variables=context_variables, model_override=None, ) - tool_calls = tool_call_message.choices[0].message.tool_calls - tool_calls_db = [ - ToolCalls( - tool_call_id=tool_call.id, - name=tool_call.function.name, - arguments=tool_call.function.arguments, - ) - for tool_call in tool_calls - ] + tool_calls = tool_call_message.output tool_calls_result = await routine.execute_tool_calls( - tool_calls=tool_calls_db, + tool_calls=tool_calls, tools=agent.tools, context_variables=context_variables, ) assert isinstance(tool_calls_result, Response) - assert tool_calls_result.messages == [ - { - "role": "tool", - "tool_call_id": tool_calls[0].id, - "tool_name": "get_weather", - "content": '{"output":{"param":"It\'s sunny today in Geneva from planet Earth."}}', - }, - { - "role": "tool", - "tool_call_id": tool_calls[1].id, - "tool_name": "get_weather", - "content": '{"output":{"param":"It\'s sunny today in Lausanne from planet Earth."}}', - }, - ] + assert len(tool_calls_result.messages) == 2 + assert tool_calls_result.messages[0].call_id == tool_calls[0].call_id + assert "Geneva" in tool_calls_result.messages[0].output + assert "Earth" in tool_calls_result.messages[0].output + assert tool_calls_result.messages[1].call_id == tool_calls[1].call_id + assert "Lausanne" in tool_calls_result.messages[1].output + assert "Earth" in tool_calls_result.messages[1].output assert tool_calls_result.agent is None assert tool_calls_result.context_variables == context_variables @@ -310,30 +291,19 @@ async def test_execute_tool_calls_handoff( context_variables=context_variables, model_override=None, ) - tool_calls = tool_call_message.choices[0].message.tool_calls - tool_calls_db = [ - ToolCalls( - tool_call_id=tool_call.id, - name=tool_call.function.name, - arguments=tool_call.function.arguments, - ) - for tool_call in tool_calls - ] + tool_calls = tool_call_message.output tool_calls_result = await routine.execute_tool_calls( - tool_calls=tool_calls_db, + tool_calls=tool_calls, tools=agent_1.tools, context_variables=context_variables, ) assert isinstance(tool_calls_result, Response) - assert tool_calls_result.messages == [ - { - "role": "tool", - "tool_call_id": tool_calls[0].id, - "tool_name": "agent_handoff_tool", - "content": json.dumps({"assistant": agent_2.name}), - } - ] + assert len(tool_calls_result.messages) == 1 + assert tool_calls_result.messages[0].call_id == tool_calls[0].call_id + assert json.loads(tool_calls_result.messages[0].output) == { + "assistant": agent_2.name + } assert tool_calls_result.agent == agent_2 assert tool_calls_result.context_variables == context_variables @@ -360,27 +330,16 @@ async def test_handle_tool_call_simple( context_variables=context_variables, model_override=None, ) - tool_call = tool_call_message.choices[0].message.tool_calls[0] - tool_call_db = ToolCalls( - tool_call_id=tool_call.id, - name=tool_call.function.name, - arguments=tool_call.function.arguments, - ) + tool_call = tool_call_message.output[0] tool_call_result = await routine.handle_tool_call( - tool_call=tool_call_db, + tool_call=tool_call, tools=agent.tools, context_variables=context_variables, ) - assert tool_call_result == ( - { - "role": "tool", - "tool_call_id": tool_call.id, - "tool_name": "get_weather", - "content": '{"output":{"param":"It\'s sunny today."}}', - }, - None, - ) + assert tool_call_result[0].call_id == tool_call.call_id + assert tool_call_result[0].output == '{"output":{"param":"It\'s sunny today."}}' + assert tool_call_result[1] is None @pytest.mark.asyncio async def test_handle_tool_call_context_var( @@ -405,230 +364,1197 @@ async def test_handle_tool_call_context_var( context_variables=context_variables, model_override=None, ) - tool_call = tool_call_message.choices[0].message.tool_calls[0] - tool_call_db = ToolCalls( - tool_call_id=tool_call.id, - name=tool_call.function.name, - arguments=tool_call.function.arguments, - ) + tool_call = tool_call_message.output[0] tool_calls_result = await routine.handle_tool_call( - tool_call=tool_call_db, + tool_call=tool_call, tools=agent.tools, context_variables=context_variables, ) - assert tool_calls_result == ( - { - "role": "tool", - "tool_call_id": tool_call.id, - "tool_name": "get_weather", - "content": '{"output":{"param":"It\'s sunny today in Geneva from planet Earth."}}', - }, - None, - ) + assert tool_calls_result[0].call_id == tool_call.call_id + assert "Geneva" in tool_calls_result[0].output + assert "Earth" in tool_calls_result[0].output + assert tool_calls_result[1] is None @pytest.mark.asyncio - async def test_handle_tool_call_handoff( + async def test_astream_complete_flow( self, mock_openai_client, get_weather_tool, agent_handoff_tool ): - routine = AgentsRoutine(client=mock_openai_client) + """Test complete astream flow with agent handoff, tool execution, and text response using Response API.""" - mock_openai_client.set_response( - create_mock_response( - message={"role": "assistant", "content": ""}, - function_calls=[{"name": "agent_handoff_tool", "args": {}}], - ), - ) - agent_1 = Agent(name="Test agent 1", tools=[agent_handoff_tool]) - agent_2 = Agent( - name="Test agent 2", tools=[get_weather_tool, agent_handoff_tool] - ) - context_variables = {"to_agent": agent_2} + # Setup agents + agent_1 = Agent(name="Agent 1", tools=[agent_handoff_tool]) + agent_2 = Agent(name="Agent 2", tools=[get_weather_tool]) - tool_call_message = await routine.get_chat_completion( - agent_1, - history=[{"role": "user", "content": "Hello"}], - context_variables=context_variables, - model_override=None, - ) - tool_call = tool_call_message.choices[0].message.tool_calls[0] - tool_call_db = ToolCalls( - tool_call_id=tool_call.id, - name=tool_call.function.name, - arguments=tool_call.function.arguments, - ) - tool_calls_result = await routine.handle_tool_call( - tool_call=tool_call_db, - tools=agent_1.tools, - context_variables=context_variables, + # Initial user message + user_msg = Messages( + thread_id="test_thread_123", + entity=Entity.USER, ) + user_msg.parts = [ + Parts( + order_index=0, + type=PartType.MESSAGE, + output={ + "content": [ + { + "text": "What's the weather like in San Francisco?", + "type": "input_text", + } + ], + "role": "user", + "status": "completed", + "type": "message", + }, + is_complete=True, + ) + ] + messages = [user_msg] + + context_variables = {"to_agent": agent_2, "planet": "Earth", "usage_dict": {}} + routine = AgentsRoutine(client=mock_openai_client) + + async def mock_streaming_completion(*args, **kwargs): + """Mock streaming responses for different turns using Response API format.""" + history = kwargs["history"] + + # Count non-tool messages to determine which turn we're on + turn = len(history) + + # Turn 1: Agent handoff + if turn == 1: + # Function call added + yield ResponseOutputItemAddedEvent( + type="response.output_item.added", + output_index=0, + sequence_number=0, + item=ResponseFunctionToolCall( + id="tc_handoff_123", + call_id="tc_random_id_1", + type="function_call", + name="agent_handoff_tool", + arguments="", + status="in_progress", + ), + ) - assert tool_calls_result == ( - { - "role": "tool", - "tool_call_id": tool_call.id, - "tool_name": "agent_handoff_tool", - "content": json.dumps({"assistant": agent_2.name}), - }, - agent_2, + # Function arguments delta + yield ResponseFunctionCallArgumentsDeltaEvent( + output_index=1, + sequence_number=1, + type="response.function_call_arguments.delta", + item_id="tc_handoff_123", + delta="{}", + ) + + # Function call done + yield ResponseOutputItemDoneEvent( + type="response.output_item.done", + output_index=2, + sequence_number=2, + item=ResponseFunctionToolCall( + id="tc_handoff_123", + call_id="tc_random_id_1", + type="function_call", + name="agent_handoff_tool", + arguments="{}", + status="completed", + ), + ) + + yield ResponseCompletedEvent( + output_index=3, + sequence_number=3, + type="response.completed", + event_id="event_4", + response=OpenAIResponse( + id="resp_1", + created_at=1234567890, + status="completed", + model="gpt-5-mini", + object="response", + parallel_tool_calls=False, + tool_choice="auto", + tools=[ + FunctionTool( + type="function", + name="agent_handoff_tool", + parameters={"to_agent": Agent}, + ) + ], + output=[ + ResponseFunctionToolCall( + id="tc_handoff_123", + call_id="tc_random_id_1", + type="function_call", + name="agent_handoff_tool", + arguments="{}", + status="completed", + ) + ], + usage=ResponseUsage( + input_tokens=50, + input_tokens_details={"cached_tokens": 0}, + output_tokens=10, + output_tokens_details={"reasoning_tokens": 0}, + total_tokens=60, + ), + ), + ) + + # Turn 2: Weather tool call + elif turn == 3: + # Function call added + yield ResponseOutputItemAddedEvent( + type="response.output_item.added", + output_index=4, + sequence_number=4, + item=ResponseFunctionToolCall( + id="tc_weather_456", + type="function_call", + call_id="tc_random_id_2", + name="get_weather", + arguments="", + status="in_progress", + ), + ) + + # Function arguments deltas + yield ResponseFunctionCallArgumentsDeltaEvent( + output_index=5, + sequence_number=5, + type="response.function_call_arguments.delta", + event_id="event_6", + item_id="tc_weather_456", + delta='{"location"', + ) + + yield ResponseFunctionCallArgumentsDeltaEvent( + output_index=6, + sequence_number=6, + type="response.function_call_arguments.delta", + event_id="event_7", + item_id="tc_weather_456", + delta=': "San Francisco"}', + ) + + # Function call done + yield ResponseOutputItemDoneEvent( + type="response.output_item.done", + output_index=7, + sequence_number=7, + item=ResponseFunctionToolCall( + id="tc_weather_456", + call_id="tc_random_id_2", + type="function_call", + name="get_weather", + arguments='{"location": "San Francisco"}', + status="completed", + ), + ) + + yield ResponseCompletedEvent( + output_index=8, + sequence_number=8, + type="response.completed", + event_id="event_6", + response=OpenAIResponse( + id="resp_2", + created_at=1234567890, + status="completed", + model="gpt-5-mini", + object="response", + parallel_tool_calls=False, + tool_choice="auto", + tools=[ + FunctionTool( + type="function", + name="get_weather_tool", + parameters={"planet": str}, + ) + ], + output=[ + ResponseFunctionToolCall( + id="tc_weather_456", + type="function_call", + call_id="tc_random_id_2", + name="get_weather", + arguments='{"location": "San Francisco"}', + status="completed", + ) + ], + usage=ResponseUsage( + input_tokens=80, + input_tokens_details={"cached_tokens": 0}, + output_tokens=80, + output_tokens_details={"reasoning_tokens": 0}, + total_tokens=95, + ), + ), + ) + + # Turn 3: Final text response + elif turn == 5: + # Message item added + yield ResponseOutputItemAddedEvent( + type="response.output_item.added", + output_index=9, + sequence_number=9, + item=ResponseOutputMessage( + id="item_text_789", + type="message", + role="assistant", + status="in_progress", + content=[], + ), + ) + + # Content part added + yield ResponseContentPartAddedEvent( + output_index=10, + sequence_number=10, + type="response.content_part.added", + event_id="event_10", + item_id="item_text_789", + content_index=42, + part=ResponseOutputText( + type="output_text", text="", annotations=[] + ), + ) + + # Text deltas + text_chunks = ["The weather ", "in San Francisco ", "is sunny today!"] + for i, chunk_text in enumerate(text_chunks): + yield ResponseTextDeltaEvent( + type="response.output_text.delta", + event_id=f"event_{11 + i}", + item_id="item_text_789", + logprobs=[], + sequence_number=11 + i, + output_index=11 + i, + content_index=42, + delta=chunk_text, + ) + + # Content part done + yield ResponseContentPartDoneEvent( + type="response.content_part.done", + event_id="event_14", + item_id="item_text_789", + sequence_number=14, + output_index=14, + content_index=42, + part=ResponseOutputText( + type="output_text", + text="The weather in San Francisco is sunny today!", + annotations=[], + ), + ) + + # Output item done + yield ResponseOutputItemDoneEvent( + type="response.output_item.done", + output_index=15, + sequence_number=15, + item=ResponseOutputMessage( + id="item_text_789", + type="message", + role="assistant", + status="completed", + content=[ + ResponseOutputText( + type="output_text", + text="The weather in San Francisco is sunny today!", + annotations=[], + ) + ], + ), + ) + + yield ResponseCompletedEvent( + output_index=16, + sequence_number=16, + type="response.completed", + event_id="event_9", + response=OpenAIResponse( + id="resp_3", + created_at=1234567890, + status="completed", + model="gpt-5-mini", + object="response", + parallel_tool_calls=False, + tool_choice="auto", + tools=[ + FunctionTool( + type="function", + name="get_weather_tool", + parameters={"planet": str}, + ) + ], + output=[ + ResponseOutputMessage( + id="item_text_789", + type="message", + role="assistant", + status="completed", + content=[ + ResponseOutputText( + type="output_text", + text="The weather in San Francisco is sunny today!", + annotations=[], + ) + ], + ) + ], + usage=ResponseUsage( + input_tokens=100, + input_tokens_details={"cached_tokens": 0}, + output_tokens=20, + output_tokens_details={"reasoning_tokens": 0}, + total_tokens=120, + ), + ), + ) + + # Collect all streamed events + events = [] + + with patch( + "neuroagent.agent_routine.AgentsRoutine.get_chat_completion", + side_effect=mock_streaming_completion, + ): + async for event in routine.astream( + agent=agent_1, + messages=messages, + context_variables=context_variables, + ): + events.append(event) + + # Parse SSE events + parsed_events = [] + for event in events: + if event.startswith("data: ") and event != "data: [DONE]\n\n": + data = event.replace("data: ", "").strip() + try: + parsed_events.append(json.loads(data)) + except json.JSONDecodeError: + pass + + # Verify event sequence + event_types = [e["type"] for e in parsed_events] + + # Expected flow for Response API: + # 1. start (initial message) + # 2. start-step + tool-input-start (handoff tool) + # 3. tool-input-available (handoff tool complete) + # 4. finish-step + # 5. tool-output-available (handoff result) + # 6. finish-step + # 7. start-step + tool-input-start (weather tool) + # 8. tool-input-delta (weather args streaming) + # 9. tool-input-available (weather tool complete) + # 10. finish-step + # 11. tool-output-available (weather result) + # 12. finish-step + # 13. text-start + # 14. text-delta (multiple) + # 15. text-end + # 16. finish-step (twice - once after text-end, once before finish) + # 17. finish + + assert "start" in event_types + assert event_types.count("start-step") == 2 # handoff + weather + assert event_types.count("tool-input-start") == 2 # handoff + weather + assert event_types.count("tool-input-available") == 2 + assert event_types.count("tool-output-available") == 2 + assert event_types.count("finish-step") >= 3 # after each turn + assert "text-start" in event_types + assert event_types.count("text-delta") >= 1 + assert "text-end" in event_types + assert "finish" in event_types + + # Verify tool calls + tool_input_events = [ + e for e in parsed_events if e["type"] == "tool-input-available" + ] + assert len(tool_input_events) == 2 + assert tool_input_events[0]["toolName"] == "agent_handoff_tool" + assert tool_input_events[1]["toolName"] == "get_weather" + assert tool_input_events[1]["input"]["location"] == "San Francisco" + + # Verify tool outputs + tool_output_events = [ + e for e in parsed_events if e["type"] == "tool-output-available" + ] + assert len(tool_output_events) == 2 + + # First output should be agent handoff + handoff_output = json.loads(tool_output_events[0]["output"]) + assert handoff_output["assistant"] == agent_2.name + + # Second output should be weather result + weather_output = tool_output_events[1]["output"] + assert "San Francisco" in weather_output + assert "Earth" in weather_output # Uses context variable + + # Verify text deltas + text_deltas = [e["delta"] for e in parsed_events if e["type"] == "text-delta"] + full_text = "".join(text_deltas) + assert full_text == "The weather in San Francisco is sunny today!" + + # Verify final message state + assert len(messages) > 1 # Original + new messages + + # Check that messages were properly recorded + ai_messages = [m for m in messages if m.entity == Entity.ASSISTANT] + assert len(ai_messages) >= 1 # At least final response + + # Verify final assistant message has the complete text + final_message = messages[-1] + assert final_message.entity == Entity.ASSISTANT + text_parts = [p for p in final_message.parts if p.type == PartType.MESSAGE] + assert len(text_parts) > 0 + assert "The weather in San Francisco is sunny today!" in str( + text_parts[-1].output ) - @pytest.mark.skip(reason="Jan was tired") + # Verify token consumption was tracked + assert any(m.token_consumption for m in messages) + @pytest.mark.asyncio - async def test_astream( - self, mock_openai_client, get_weather_tool, agent_handoff_tool - ): - agent_1 = Agent(name="Test Agent", tools=[agent_handoff_tool]) - agent_2 = Agent(name="Test Agent", tools=[get_weather_tool]) - messages = [ - Messages( - thread_id="fake_id", - entity=Entity.USER, - content=json.dumps( - { - "role": "user", - "content": { - "role": "user", - "content": "What's the weather like in San Francisco?", - }, - } - ), + async def test_astream_max_turns_limit(self, mock_openai_client, get_weather_tool): + """Test that max_turns limit is enforced.""" + agent = Agent(name="Test Agent", tools=[get_weather_tool]) + user_msg = Messages( + thread_id="test_thread", + entity=Entity.USER, + ) + user_msg.parts = [ + Parts( + order_index=0, + type=PartType.MESSAGE, + output={ + "content": [{"text": "Test", "type": "input_text"}], + "role": "user", + "status": "completed", + "type": "message", + }, + is_complete=True, ) ] - context_variables = {"to_agent": agent_2, "planet": "Mars"} - routine = AgentsRoutine(client=mock_openai_client) + messages = [user_msg] + context_variables = {"usage_dict": {}} - async def return_iterator(*args, **kwargs): - async def mock_openai_streaming_response( - history, - ) -> AsyncIterator[ChatCompletionChunk]: - """ - Simulates streaming chunks of a response for patching. - - Yields - ------ - AsyncIterator[ChatCompletionChunk]: Streaming chunks of the response. - """ - responses = [ - { - "message": {"role": "assistant", "content": ""}, - "function_call": [{"name": "agent_handoff_tool", "args": {}}], - }, - { - "message": {"role": "assistant", "content": ""}, - "function_call": [ - {"name": "get_weather", "args": {"location": "Montreux"}} + async def mock_tool_calls(*args, **kwargs): + """Always return tool calls to trigger max turns.""" + history = kwargs["history"] + turn = len(history) + + if turn == 1: + yield ResponseOutputItemAddedEvent( + type="response.output_item.added", + output_index=0, + sequence_number=0, + item=ResponseFunctionToolCall( + id="tc_123", + call_id="tc_random_1", + type="function_call", + name="get_weather", + arguments='{"location": "NYC"}', + status="in_progress", + ), + ) + + yield ResponseOutputItemDoneEvent( + type="response.output_item.done", + output_index=1, + sequence_number=1, + item=ResponseFunctionToolCall( + id="tc_123", + call_id="tc_random_1", + type="function_call", + name="get_weather", + arguments='{"location": "NYC"}', + status="completed", + ), + ) + + yield ResponseCompletedEvent( + output_index=2, + sequence_number=2, + type="response.completed", + event_id="event_1", + response=OpenAIResponse( + id="resp_1", + created_at=1234567890, + status="completed", + model="gpt-5-mini", + object="response", + parallel_tool_calls=False, + tool_choice="auto", + tools=[], + output=[ + ResponseFunctionToolCall( + id="tc_123", + call_id="tc_random_1", + type="function_call", + name="get_weather", + arguments='{"location": "NYC"}', + status="completed", + ) ], - }, - { - "message": { - "role": "assistant", - "content": "sample response content", - }, - }, - ] - response_to_call = ( - len([hist for hist in history if hist["role"] != "tool"]) - 1 + usage=ResponseUsage( + input_tokens=50, + input_tokens_details={"cached_tokens": 0}, + output_tokens=10, + output_tokens_details={"reasoning_tokens": 0}, + total_tokens=60, + ), + ), + ) + + elif turn == 3: + yield ResponseOutputItemAddedEvent( + type="response.output_item.added", + output_index=3, + sequence_number=3, + item=ResponseOutputMessage( + id="item_text_1", + type="message", + role="assistant", + status="in_progress", + content=[], + ), + ) + + yield ResponseContentPartAddedEvent( + output_index=4, + sequence_number=4, + type="response.content_part.added", + event_id="event_2", + item_id="item_text_1", + content_index=0, + part=ResponseOutputText( + type="output_text", text="", annotations=[] + ), ) - response = responses[response_to_call] - - if "message" in response and "content" in response["message"]: - content = response["message"]["content"] - for i in range( - 0, len(content), 10 - ): # Stream content in chunks of 10 chars - yield ChatCompletionChunk( - id="chatcmpl-AdfVmbjxczsgRAADk9pXkmKPFsikY", - choices=[ - Choice( - delta=ChoiceDelta(content=content[i : i + 10]), - finish_reason=None, - index=0, - ) - ], - created=1734017726, - model="gpt-5-mini-2024-07-18", - object="chat.completion.chunk", - system_fingerprint="fp_bba3c8e70b", - ) - if "function_call" in response: - for function_call in response["function_call"]: - yield ChatCompletionChunk( - id="chatcmpl-AdfVmbjxczsgRAADk9pXkmKPFsikY", - choices=[ - Choice( - delta=ChoiceDelta( - tool_calls=[ - ChoiceDeltaToolCall( - index=0, - id="mock_tc_id", - function=ChoiceDeltaToolCallFunction( - arguments=json.dumps( - function_call["args"] - ), - name=function_call["name"], - ), - type="function", - ) - ] - ), - finish_reason=None, - index=0, + text_chunks = ["The weather ", "in San Francisco ", "is sunny today!"] + for i, chunk_text in enumerate(text_chunks): + yield ResponseTextDeltaEvent( + type="response.output_text.delta", + event_id=f"event_{5 + i}", + item_id="item_text_1", + logprobs=[], + sequence_number=5 + i, + output_index=5 + i, + content_index=0, + delta=chunk_text, + ) + + yield ResponseContentPartDoneEvent( + type="response.content_part.done", + event_id="event_8", + item_id="item_text_1", + sequence_number=8, + output_index=8, + content_index=0, + part=ResponseOutputText( + type="output_text", + text="The weather in San Francisco is sunny today!", + annotations=[], + ), + ) + + yield ResponseCompletedEvent( + output_index=9, + sequence_number=9, + type="response.completed", + event_id="event_8", + response=OpenAIResponse( + id="resp_2", + created_at=1234567892, + status="completed", + model="gpt-5-mini", + object="response", + parallel_tool_calls=False, + tool_choice="auto", + tools=[], + output=[ + ResponseOutputMessage( + id="item_text_1", + type="message", + role="assistant", + status="completed", + content=[ + ResponseOutputText( + type="output_text", + text="The weather in San Francisco is sunny today!", + annotations=[], + ) + ], + ) + ], + usage=ResponseUsage( + input_tokens=100, + input_tokens_details={"cached_tokens": 0}, + output_tokens=20, + output_tokens_details={"reasoning_tokens": 0}, + total_tokens=120, + ), + ), + ) + + routine = AgentsRoutine(client=mock_openai_client) + events = [] + + with patch( + "neuroagent.agent_routine.AgentsRoutine.get_chat_completion", + side_effect=mock_tool_calls, + ): + async for event in routine.astream( + agent=agent, + messages=messages, + context_variables=context_variables, + max_turns=2, + ): + events.append(event) + + parsed_events = [] + for event in events: + if event.startswith("data: ") and event != "data: [DONE]\n\n": + data = event.replace("data: ", "").strip() + try: + parsed_events.append(json.loads(data)) + except json.JSONDecodeError: + pass + + event_types = [e["type"] for e in parsed_events] + assert "text-delta" in event_types or "text-start" in event_types + + @pytest.mark.asyncio + async def test_astream_with_reasoning(self, mock_openai_client): + """Test streaming with reasoning tokens (for o1-style models).""" + agent = Agent(name="Reasoning Agent", tools=[], model="gpt-5-mini") + user_msg = Messages( + thread_id="test_thread", + entity=Entity.USER, + ) + user_msg.parts = [ + Parts( + order_index=0, + type=PartType.MESSAGE, + output={ + "content": [{"text": "Solve this problem", "type": "input_text"}], + "role": "user", + "status": "completed", + "type": "message", + }, + is_complete=True, + ) + ] + messages = [user_msg] + context_variables = {"usage_dict": {}} + + async def mock_reasoning_response(*args, **kwargs): + """Mock response with reasoning tokens.""" + from openai.types.responses import ResponseReasoningItem + + yield ResponseOutputItemAddedEvent( + type="response.output_item.added", + output_index=0, + sequence_number=0, + item=ResponseReasoningItem( + id="item_reason_1", + type="reasoning", + summary=[], + ), + ) + + yield ResponseReasoningSummaryPartAddedEvent( + type="response.reasoning_summary_part.added", + event_id="event_1", + item_id="item_reason_1", + output_index=1, + content_index=0, + sequence_number=1, + summary_index=42, + part={"type": "summary_text", "text": ""}, + ) + + reasoning_parts = ["Let me think", " about this", " carefully"] + for i, part in enumerate(reasoning_parts): + yield ResponseReasoningSummaryTextDeltaEvent( + type="response.reasoning_summary_text.delta", + event_id=f"event_{2 + i}", + item_id="item_reason_1", + output_index=2 + i, + sequence_number=2 + i, + content_index=0, + summary_index=42, + delta=part, + ) + + yield ResponseReasoningSummaryPartDoneEvent( + type="response.reasoning_summary_part.done", + event_id="event_5", + item_id="item_reason_1", + output_index=5, + content_index=0, + sequence_number=5, + summary_index=42, + part={ + "type": "summary_text", + "text": "Let me think about this carefully", + }, + ) + + yield ResponseOutputItemAddedEvent( + type="response.output_item.added", + output_index=6, + sequence_number=6, + item=ResponseOutputMessage( + id="item_text_1", + type="message", + role="assistant", + status="in_progress", + content=[], + ), + ) + + yield ResponseContentPartAddedEvent( + output_index=7, + sequence_number=7, + type="response.content_part.added", + event_id="event_6", + item_id="item_text_1", + content_index=0, + part=ResponseOutputText(type="output_text", text="", annotations=[]), + ) + + yield ResponseTextDeltaEvent( + type="response.output_text.delta", + event_id="event_8", + item_id="item_text_1", + logprobs=[], + sequence_number=8, + output_index=8, + content_index=0, + delta="Here's the solution", + ) + + yield ResponseContentPartDoneEvent( + type="response.content_part.done", + event_id="event_9", + item_id="item_text_1", + sequence_number=9, + output_index=9, + content_index=0, + part=ResponseOutputText( + type="output_text", text="Here's the solution", annotations=[] + ), + ) + + yield ResponseCompletedEvent( + output_index=10, + sequence_number=10, + type="response.completed", + event_id="event_9", + response=OpenAIResponse( + id="resp_1", + created_at=1234567890, + status="completed", + model="gpt-5-mini", + object="response", + parallel_tool_calls=False, + tool_choice="auto", + tools=[], + output=[ + ResponseOutputMessage( + id="item_text_1", + type="message", + role="assistant", + status="completed", + content=[ + ResponseOutputText( + type="output_text", + text="Here's the solution", + annotations=[], ) ], - created=1734017726, - model="gpt-5-mini-2024-07-18", - object="chat.completion.chunk", - system_fingerprint="fp_bba3c8e70b", ) - - yield ChatCompletionChunk( - id="chatcmpl-AdfVmbjxczsgRAADk9pXkmKPFsikY", - choices=[ - Choice(delta=ChoiceDelta(), finish_reason="stop", index=0) ], - created=1734017726, - model="gpt-5-mini-2024-07-18", - object="chat.completion.chunk", - system_fingerprint="fp_bba3c8e70b", - ) + usage=ResponseUsage( + input_tokens=10, + input_tokens_details={"cached_tokens": 0}, + output_tokens=20, + output_tokens_details={"reasoning_tokens": 0}, + total_tokens=30, + ), + ), + ) - return mock_openai_streaming_response(kwargs["history"]) + routine = AgentsRoutine(client=mock_openai_client) + events = [] - tokens = [] with patch( "neuroagent.agent_routine.AgentsRoutine.get_chat_completion", - new=return_iterator, + side_effect=mock_reasoning_response, ): - async for token in routine.astream( - agent=agent_1, messages=messages, context_variables=context_variables + async for event in routine.astream( + agent=agent, + messages=messages, + context_variables=context_variables, ): - if isinstance(token, str): - tokens.append(token) - else: - response = token - - expected_tokens = [ - "b:{'toolCallId':mock_tc_id,'toolName':agent_handoff_tool}\n", - "c:{toolCallId:mock_tc_id; argsTextDelta:{}}\n", - "b:{'toolCallId':mock_tc_id,'toolName':get_weather}\n", - 'c:{toolCallId:mock_tc_id; argsTextDelta:{"location": "Montreux"}}\n', - '0:"sample res"\n', - '0:"ponse cont"\n', - '0:"ent"\n', + events.append(event) + + parsed_events = [] + for event in events: + if event.startswith("data: ") and event != "data: [DONE]\n\n": + data = event.replace("data: ", "").strip() + try: + parsed_events.append(json.loads(data)) + except json.JSONDecodeError: + pass + + event_types = [e["type"] for e in parsed_events] + assert "reasoning-start" in event_types + assert "reasoning-delta" in event_types + assert "reasoning-end" in event_types + + reasoning_deltas = [ + e["delta"] for e in parsed_events if e["type"] == "reasoning-delta" ] - assert "".join(tokens) == "".join(expected_tokens) - assert response.messages[2]["role"] == "tool" - assert response.messages[2]["content"] == json.dumps( - {"assistant": agent_1.name} + full_reasoning = "".join(reasoning_deltas) + assert "Let me think about this carefully" == full_reasoning + + @pytest.mark.asyncio + async def test_astream_hil_tool_validation( + self, mock_openai_client, get_weather_tool, agent_handoff_tool + ): + """Test Human-in-the-Loop tool validation.""" + get_weather_tool.hil = True + agent = Agent(name="Test Agent", tools=[get_weather_tool]) + user_msg = Messages( + thread_id="test_thread", + entity=Entity.USER, ) - assert response.messages[-2]["role"] == "tool" - assert ( - response.messages[-2]["content"] - == "It's sunny today in Montreux from planet Mars." + user_msg.parts = [ + Parts( + order_index=0, + type=PartType.MESSAGE, + output={ + "content": [{"text": "Weather check", "type": "input_text"}], + "role": "user", + "status": "completed", + "type": "message", + }, + is_complete=True, + ) + ] + messages = [user_msg] + context_variables = {"usage_dict": {}} + + async def mock_tool_call(*args, **kwargs): + """Mock a tool call.""" + yield ResponseOutputItemAddedEvent( + type="response.output_item.added", + output_index=0, + sequence_number=0, + item=ResponseFunctionToolCall( + id="tc_hil", + call_id="tc_random_hil", + type="function_call", + name="get_weather", + arguments='{"location": "Paris"}', + status="in_progress", + ), + ) + + yield ResponseOutputItemDoneEvent( + type="response.output_item.done", + output_index=1, + sequence_number=1, + item=ResponseFunctionToolCall( + id="tc_hil", + call_id="tc_random_hil", + type="function_call", + name="get_weather", + arguments='{"location": "Paris"}', + status="completed", + ), + ) + + yield ResponseCompletedEvent( + output_index=2, + sequence_number=2, + type="response.completed", + event_id="event_1", + response=OpenAIResponse( + id="resp_1", + created_at=1234567890, + status="completed", + model="gpt-5-mini", + object="response", + parallel_tool_calls=False, + tool_choice="auto", + tools=[], + output=[ + ResponseFunctionToolCall( + id="tc_hil", + call_id="tc_random_hil", + type="function_call", + name="get_weather", + arguments='{"location": "Paris"}', + status="completed", + ) + ], + usage=ResponseUsage( + input_tokens=50, + input_tokens_details={"cached_tokens": 0}, + output_tokens=10, + output_tokens_details={"reasoning_tokens": 0}, + total_tokens=60, + ), + ), + ) + + routine = AgentsRoutine(client=mock_openai_client) + events = [] + + with patch( + "neuroagent.agent_routine.AgentsRoutine.get_chat_completion", + side_effect=mock_tool_call, + ): + async for event in routine.astream( + agent=agent, + messages=messages, + context_variables=context_variables, + ): + events.append(event) + + parsed_events = [] + for event in events: + if event.startswith("data: ") and event != "data: [DONE]\n\n": + data = event.replace("data: ", "").strip() + try: + parsed_events.append(json.loads(data)) + except json.JSONDecodeError: + pass + + finish_events = [e for e in parsed_events if e["type"] == "finish"] + assert len(finish_events) == 1 + finish_event = finish_events[0] + assert "messageMetadata" in finish_event + assert "toolCalls" in finish_event["messageMetadata"] + hil_data = finish_event["messageMetadata"]["toolCalls"] + assert len(hil_data) == 1 + assert hil_data[0]["validated"] == "pending" + + @pytest.mark.asyncio + async def test_astream_parallel_tool_call_limit( + self, mock_openai_client, get_weather_tool + ): + """Test that parallel tool calls are limited.""" + + agent = Agent( + name="Test Agent", tools=[get_weather_tool], parallel_tool_calls=True ) - assert response.messages[-1]["role"] == "assistant" - assert response.messages[-1]["content"] == "sample response content" - assert response.agent == agent_2 - assert response.context_variables == context_variables + user_msg = Messages( + thread_id="test_thread", + entity=Entity.USER, + ) + user_msg.parts = [ + Parts( + order_index=0, + type=PartType.MESSAGE, + output={ + "content": [ + {"text": "Check multiple cities", "type": "input_text"} + ], + "role": "user", + "status": "completed", + "type": "message", + }, + is_complete=True, + ) + ] + messages = [user_msg] + context_variables = {"usage_dict": {}} + + async def mock_multiple_tool_calls(*args, **kwargs): + """Mock multiple parallel tool calls.""" + history = kwargs["history"] + turn = len(history) + + if turn == 1: + # First chunk with tool call start + for i in range(3): # 3 tool calls + yield ResponseOutputItemAddedEvent( + type="response.output_item.added", + output_index=i, + sequence_number=i, + item=ResponseFunctionToolCall( + id=f"tc_{i}", + call_id=f"tc_random_{i}", + type="function_call", + name="get_weather", + arguments="", + status="in_progress", + ), + ) + + yield ResponseFunctionCallArgumentsDeltaEvent( + output_index=i + 3, + sequence_number=i + 3, + type="response.function_call_arguments.delta", + item_id=f"tc_{i}", + delta=f'{{"location": "City{i}"}}', + ) + + yield ResponseOutputItemDoneEvent( + type="response.output_item.done", + output_index=i + 6, + sequence_number=i + 6, + item=ResponseFunctionToolCall( + id=f"tc_{i}", + call_id=f"tc_random_{i}", + type="function_call", + name="get_weather", + arguments=f'{{"location": "City{i}"}}', + status="completed", + ), + ) + + yield ResponseCompletedEvent( + output_index=9, + sequence_number=9, + type="response.completed", + event_id="event_1", + response=OpenAIResponse( + id="resp_1", + created_at=1234567890, + status="completed", + model="gpt-5-mini", + object="response", + parallel_tool_calls=True, + tool_choice="auto", + tools=[ + FunctionTool( + type="function", + name="get_weather", + parameters={"location": str}, + ) + ], + output=[ + ResponseFunctionToolCall( + id=f"tc_{i}", + call_id=f"tc_random_{i}", + type="function_call", + name="get_weather", + arguments=f'{{"location": "City{i}"}}', + status="completed", + ) + for i in range(3) + ], + usage=ResponseUsage( + input_tokens=50, + input_tokens_details={"cached_tokens": 0}, + output_tokens=30, + output_tokens_details={"reasoning_tokens": 0}, + total_tokens=80, + ), + ), + ) + + # Second turn - final response + elif turn == 5: + yield ResponseContentPartAddedEvent( + output_index=10, + sequence_number=10, + type="response.content_part.added", + event_id="event_2", + item_id="item_final", + content_index=0, + part=ResponseOutputText( + type="output_text", text="", annotations=[] + ), + ) + + yield ResponseTextDeltaEvent( + type="response.output_text.delta", + event_id="event_3", + item_id="item_final", + logprobs=[], + sequence_number=11, + output_index=11, + content_index=0, + delta="Done", + ) + + yield ResponseContentPartDoneEvent( + type="response.content_part.done", + event_id="event_4", + item_id="item_final", + sequence_number=12, + output_index=12, + content_index=0, + part=ResponseOutputText( + type="output_text", text="Done", annotations=[] + ), + ) + + yield ResponseCompletedEvent( + output_index=13, + sequence_number=13, + type="response.completed", + event_id="event_5", + response=OpenAIResponse( + id="resp_2", + created_at=1234567891, + status="completed", + model="gpt-5-mini", + object="response", + parallel_tool_calls=False, + tool_choice="auto", + tools=[], + output=[ + ResponseOutputMessage( + id="item_final", + type="message", + role="assistant", + status="completed", + content=[ + ResponseOutputText( + type="output_text", text="Done", annotations=[] + ) + ], + ) + ], + usage=ResponseUsage( + input_tokens=80, + input_tokens_details={"cached_tokens": 0}, + output_tokens=5, + output_tokens_details={"reasoning_tokens": 0}, + total_tokens=85, + ), + ), + ) + + routine = AgentsRoutine(client=mock_openai_client) + events = [] + + with patch( + "neuroagent.agent_routine.AgentsRoutine.get_chat_completion", + side_effect=mock_multiple_tool_calls, + ): + async for event in routine.astream( + agent=agent, + messages=messages, + context_variables=context_variables, + max_parallel_tool_calls=2, # Limit to 2 + ): + events.append(event) + + # Check that messages were created + assert len(messages) > 1 diff --git a/backend/tests/test_utils.py b/backend/tests/test_utils.py index 2c00bdbd3..52d03a673 100644 --- a/backend/tests/test_utils.py +++ b/backend/tests/test_utils.py @@ -5,81 +5,20 @@ import pytest +from neuroagent.app.database.sql_schemas import PartType, Task from neuroagent.utils import ( + append_part, complete_partial_json, delete_from_storage, - merge_chunk, - merge_fields, + get_main_LLM_token_consumption, + get_previous_hil_metadata, + get_token_count, + get_tool_token_consumption, + messages_to_openai_content, save_to_storage, ) -def test_merge_fields_str(): - target = {"key_1": "abc", "key_2": ""} - source = {"key_1": "def"} - merge_fields(target, source) - assert target == {"key_1": "abcdef", "key_2": ""} - - source = {"key_1": "", "key_2": ""} - target = {"key_1": "value_1"} - with pytest.raises(KeyError): - merge_fields(target, source) - - -def test_merge_fields_dict(): - target = {"key_1": "abc", "key_2": {"sub_key_1": "", "sub_key_2": "abc"}} - source = {"key_1": "def", "key_2": {"sub_key_1": "hello", "sub_key_2": "cba"}} - merge_fields(target, source) - assert target == { - "key_1": "abcdef", - "key_2": {"sub_key_1": "hello", "sub_key_2": "abccba"}, - } - - -def test_merge_chunk(): - message = { - "content": "", - "sender": "test agent", - "role": "assistant", - "function_call": None, - "tool_calls": [ - { - "function": {"arguments": "", "name": ""}, - "id": "", - "type": "", - } - ], - } - delta = { - "content": "Great content", - "function_call": None, - "refusal": None, - "role": "assistant", - "tool_calls": [ - { - "index": 0, - "id": "call_NDiPAjDW4oLef44xIptVSAZC", - "function": {"arguments": "Thalamus", "name": "resolve-entities-tool"}, - "type": "function", - } - ], - } - merge_chunk(message, delta) - assert message == { - "content": "Great content", - "sender": "test agent", - "role": "assistant", - "function_call": None, - "tool_calls": [ - { - "function": {"arguments": "Thalamus", "name": "resolve-entities-tool"}, - "id": "call_NDiPAjDW4oLef44xIptVSAZC", - "type": "function", - } - ], - } - - @pytest.mark.parametrize( "partial", [ @@ -368,3 +307,141 @@ def test_delete_from_storage_large_batch(): # Second batch should have 500 objects second_batch = mock_s3.delete_objects.call_args_list[1][1] assert len(second_batch["Delete"]["Objects"]) == 500 + + +@pytest.mark.asyncio +async def test_messages_to_openai_content(): + # Create mock messages with parts + mock_part1 = Mock() + mock_part1.output = {"role": "user", "content": "Hello"} + + mock_part2 = Mock() + mock_part2.output = {"role": "assistant", "content": "Hi there"} + + mock_message1 = Mock() + mock_message1.parts = [mock_part1] + + mock_message2 = Mock() + mock_message2.parts = [mock_part2] + + db_messages = [mock_message1, mock_message2] + + result = await messages_to_openai_content(db_messages) + + assert len(result) == 2 + assert result[0] == {"role": "user", "content": "Hello"} + assert result[1] == {"role": "assistant", "content": "Hi there"} + + +def test_get_token_count(): + # Test with usage data + mock_usage = Mock() + mock_usage.input_tokens = 100 + mock_usage.output_tokens = 50 + mock_usage.input_tokens_details = Mock() + mock_usage.input_tokens_details.cached_tokens = 20 + + result = get_token_count(mock_usage) + + assert result["input_cached"] == 20 + assert result["input_noncached"] == 80 + assert result["completion"] == 50 + + # Test with None + result_none = get_token_count(None) + assert result_none == { + "input_cached": None, + "input_noncached": None, + "completion": None, + } + + +def test_append_part(): + mock_message = Mock() + mock_message.message_id = "msg-123" + mock_message.parts = [] + + mock_openai_part = Mock() + mock_openai_part.model_dump.return_value = {"type": "message", "content": "test"} + + history = [] + + append_part( + mock_message, history, mock_openai_part, PartType.MESSAGE, is_complete=True + ) + + assert len(mock_message.parts) == 1 + assert len(history) == 1 + assert history[0] == {"type": "message", "content": "test"} + + +def test_get_main_LLM_token_consumption(): + mock_usage = Mock() + mock_usage.input_tokens = 150 + mock_usage.output_tokens = 75 + mock_details = Mock() + mock_details.cached_tokens = 30 + mock_usage.input_tokens_details = mock_details + + result = get_main_LLM_token_consumption(mock_usage, "gpt-4", Task.CHAT_COMPLETION) + + assert len(result) == 3 + assert all(tc.model == "gpt-4" for tc in result) + assert all(tc.task == Task.CHAT_COMPLETION for tc in result) + + # Test with None + result_none = get_main_LLM_token_consumption(None, "gpt-4", Task.CHAT_COMPLETION) + assert result_none == [] + + +def test_get_tool_token_consumption(): + mock_tool_response = Mock() + mock_tool_response.call_id = "call-123" + + context_variables = { + "usage_dict": { + "call-123": { + "model": "gpt-4", + "input_cached": 10, + "input_noncached": 50, + "completion": 25, + } + } + } + + result = get_tool_token_consumption(mock_tool_response, context_variables) + + assert len(result) == 3 + assert all(tc.task == Task.CALL_WITHIN_TOOL for tc in result) + assert all(tc.model == "gpt-4" for tc in result) + + # Test with missing call_id + context_empty = {"usage_dict": {}} + result_empty = get_tool_token_consumption(mock_tool_response, context_empty) + assert result_empty == [] + + +def test_get_previous_hil_metadata(): + mock_message = Mock() + + mock_part1 = Mock() + mock_part1.type = PartType.FUNCTION_CALL + mock_part1.output = {"name": "tool1", "call_id": "call-1"} + mock_part1.validated = True + mock_part1.is_complete = True + + mock_part2 = Mock() + mock_part2.type = PartType.MESSAGE + + mock_message.parts = [mock_part1, mock_part2] + + mock_tool = Mock() + mock_tool.hil = True + tool_map = {"tool1": mock_tool} + + result = get_previous_hil_metadata(mock_message, tool_map) + + assert len(result) == 1 + assert result[0]["toolCallId"] == "call-1" + assert result[0]["validated"] == "accepted" + assert result[0]["isComplete"] is True diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d2e01f2a9..fd78bbd0b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,10 +8,12 @@ "name": "frontend", "version": "0.1.0", "dependencies": { - "@ai-sdk/react": "^1.2.12", + "@ai-sdk/openai": "^2.0.52", + "@ai-sdk/react": "^2.0.72", "@bprogress/next": "^3.0.4", "@hookform/resolvers": "^3.10.0", "@nanostores/react": "github:ai/react", + "@next/env": "^15.5.5", "@radix-ui/react-checkbox": "^1.1.3", "@radix-ui/react-collapsible": "^1.1.2", "@radix-ui/react-dialog": "^1.1.6", @@ -21,10 +23,10 @@ "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-tooltip": "^1.1.8", - "@t3-oss/env-nextjs": "^0.12.0", "@tailwindcss/line-clamp": "^0.4.4", "@tailwindcss/typography": "^0.5.16", "@tanstack/react-query": "^5.66.0", + "ai": "^5.0.72", "buffer": "^6.0.3", "chart.js": "^4.4.7", "class-variance-authority": "^0.7.1", @@ -38,8 +40,6 @@ "next": "15.2.4", "next-auth": "^4.24.11", "next-themes": "^0.4.6", - "plotly.js": "^3.1.1", - "plotly.js-dist-min": "^3.1.1", "rand-seed": "^2.1.7", "react": "^19.0.0", "react-chartjs-2": "^5.3.0", @@ -52,7 +52,7 @@ "sonner": "^2.0.1", "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7", - "zod": "^3.24.1", + "zod": "^4.1.12", "zustand": "^5.0.3" }, "devDependencies": { @@ -62,7 +62,6 @@ "@testing-library/user-event": "^14.6.1", "@types/js-cookie": "^3.0.6", "@types/node": "^20", - "@types/plotly.js": "^3.0.7", "@types/react": "^19", "@types/react-dom": "^19", "@types/react-plotly.js": "^2.6.3", @@ -80,16 +79,49 @@ } }, "node_modules/@adobe/css-tools": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.1.tgz", - "integrity": "sha512-12WGKBQzjUAI4ayyF4IAtfw2QR/IDoqk6jTddXDhtYTJF9ASmoE1zst7cVtP0aL/F1jUJL5r+JxKXKEgHNbEUQ==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", "dev": true, "license": "MIT" }, + "node_modules/@ai-sdk/gateway": { + "version": "1.0.40", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-1.0.40.tgz", + "integrity": "sha512-zlixM9jac0w0jjYl5gwNq+w9nydvraAmLaZQbbh+QpHU+OPkTIZmyBcKeTq5eGQKQxhi+oquHxzCSKyJx3egGw==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.12", + "@vercel/oidc": "3.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/openai": { + "version": "2.0.52", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-2.0.52.tgz", + "integrity": "sha512-n1arAo4+63e6/FFE6z/1ZsZbiOl4cfsoZ3F4i2X7LPIEea786Y2yd7Qdr7AdB4HTLVo3OSb1PHVIcQmvYIhmEA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.12" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, "node_modules/@ai-sdk/provider": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-1.1.3.tgz", - "integrity": "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", "license": "Apache-2.0", "dependencies": { "json-schema": "^0.4.0" @@ -99,30 +131,30 @@ } }, "node_modules/@ai-sdk/provider-utils": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-2.2.8.tgz", - "integrity": "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.12.tgz", + "integrity": "sha512-ZtbdvYxdMoria+2SlNarEk6Hlgyf+zzcznlD55EAl+7VZvJaSg2sqPvwArY7L6TfDEDJsnCq0fdhBSkYo0Xqdg==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "1.1.3", - "nanoid": "^3.3.8", - "secure-json-parse": "^2.7.0" + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.5" }, "engines": { "node": ">=18" }, "peerDependencies": { - "zod": "^3.23.8" + "zod": "^3.25.76 || ^4.1.8" } }, "node_modules/@ai-sdk/react": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-1.2.12.tgz", - "integrity": "sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==", + "version": "2.0.72", + "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-2.0.72.tgz", + "integrity": "sha512-3BdjktQd/k7oulaAjVJcati6Iw2GtKPpqbEpJ/lrRohbjM2EhrDkOXOxzPcHwNrVOznIp/EjGPmt6i9dzPXEAg==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider-utils": "2.2.8", - "@ai-sdk/ui-utils": "1.2.11", + "@ai-sdk/provider-utils": "3.0.12", + "ai": "5.0.72", "swr": "^2.2.5", "throttleit": "2.1.0" }, @@ -131,7 +163,7 @@ }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", - "zod": "^3.23.8" + "zod": "^3.25.76 || ^4.1.8" }, "peerDependenciesMeta": { "zod": { @@ -139,25 +171,10 @@ } } }, - "node_modules/@ai-sdk/ui-utils": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@ai-sdk/ui-utils/-/ui-utils-1.2.11.tgz", - "integrity": "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "1.1.3", - "@ai-sdk/provider-utils": "2.2.8", - "zod-to-json-schema": "^3.24.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.23.8" - } - }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", "license": "MIT", "engines": { "node": ">=10" @@ -166,53 +183,46 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@asamuzakjp/css-color": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-2.8.3.tgz", - "integrity": "sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", "dev": true, "license": "MIT", "dependencies": { - "@csstools/css-calc": "^2.1.1", - "@csstools/css-color-parser": "^3.0.7", + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", - "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", "dev": true, "license": "MIT", "engines": { @@ -220,23 +230,23 @@ } }, "node_modules/@babel/core": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.7.tgz", - "integrity": "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.5", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.7", - "@babel/parser": "^7.26.7", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.26.7", - "@babel/types": "^7.26.7", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -251,40 +261,17 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/generator": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", - "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.5", - "@babel/types": "^7.26.5", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -292,14 +279,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", - "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.5", - "@babel/helper-validator-option": "^7.25.9", + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -308,50 +295,40 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -361,9 +338,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", - "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, "license": "MIT", "engines": { @@ -371,9 +348,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", "engines": { @@ -381,9 +358,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "dev": true, "license": "MIT", "engines": { @@ -391,9 +368,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "license": "MIT", "engines": { @@ -401,27 +378,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", - "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.7" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", - "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.7" + "@babel/types": "^7.28.4" }, "bin": { "parser": "bin/babel-parser.js" @@ -431,13 +408,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", - "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -447,13 +424,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", - "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -463,89 +440,76 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz", - "integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", - "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.5", - "@babel/parser": "^7.26.7", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.7", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/types": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", - "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@bprogress/core": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@bprogress/core/-/core-1.1.15.tgz", - "integrity": "sha512-s5O7RjLwD8YXa8tUCQhm/VIc/yTifAlCiOBX2ejCM3e+HSAiEXEvYiEgslSdnSjw/wKl3myLzvO+DLHuBzO+IQ==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@bprogress/core/-/core-1.3.4.tgz", + "integrity": "sha512-q/AqpurI/1uJzOrQROuZWixn/+ARekh+uvJGwLCP6HQ/EqAX4SkvNf618tSBxL4NysC0MwqAppb/mRw6Tzi61w==", "license": "MIT" }, "node_modules/@bprogress/next": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@bprogress/next/-/next-3.0.4.tgz", - "integrity": "sha512-FAxrYcdDPwPwoEtTyufquUTi7lRgADE2bio0oJokIs9Z4gzdKECJxE582hV9e77euDvog0zcHOS4iC4Rhfyr4Q==", + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/@bprogress/next/-/next-3.2.12.tgz", + "integrity": "sha512-/ZvNwbAd0ty9QiQwCfT2AfwWVdAaEyCPx5RUz3CfiiJS/OLBohhDz/IC/srhwK9GnXeXavvtiUrpKzN5GJDwlw==", "license": "MIT", "dependencies": { - "@bprogress/core": "^1.1.15", - "@bprogress/react": "^1.0.1" + "@bprogress/core": "^1.3.4", + "@bprogress/react": "^1.2.7" }, "peerDependencies": { "next": ">=13.0.0", @@ -554,12 +518,12 @@ } }, "node_modules/@bprogress/react": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@bprogress/react/-/react-1.0.2.tgz", - "integrity": "sha512-E2VdABlVz7evjcj1D3pASOw5fDt/RaJpGIBZocxNahabRLXFbLItDVwArcKlgmlXhzwXAiHHWoK3fHYKMXiI8w==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@bprogress/react/-/react-1.2.7.tgz", + "integrity": "sha512-MqJfHW+R5CQeWqyqrLxUjdBRHk24Xl63OkBLo5DMWqUqocUikRTfCIc/jtQQbPk7BRfdr5OP3Lx7YlfQ9QOZMQ==", "license": "MIT", "dependencies": { - "@bprogress/core": "^1.1.15" + "@bprogress/core": "^1.3.4" }, "peerDependencies": { "react": ">=18.0.0", @@ -585,9 +549,9 @@ "license": "MIT" }, "node_modules/@csstools/color-helpers": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.1.tgz", - "integrity": "sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", "dev": true, "funding": [ { @@ -605,9 +569,9 @@ } }, "node_modules/@csstools/css-calc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.1.tgz", - "integrity": "sha512-rL7kaUnTkL9K+Cvo2pnCieqNpTKgQzy5f+N+5Iuko9HAoasP+xgprVh7KN/MaJVvVL1l0EzQq2MoqBHKSrDrag==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", "dev": true, "funding": [ { @@ -624,14 +588,14 @@ "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/css-color-parser": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.7.tgz", - "integrity": "sha512-nkMp2mTICw32uE5NN+EsJ4f5N+IGFeCFu4bGpiKgb2Pq/7J/MpyLBeQ5ry4KKtRFZaYs6sTmcMYrSRIyj5DFKA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", "dev": true, "funding": [ { @@ -645,21 +609,21 @@ ], "license": "MIT", "dependencies": { - "@csstools/color-helpers": "^5.0.1", - "@csstools/css-calc": "^2.1.1" + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" }, "engines": { "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", - "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", "dev": true, "funding": [ { @@ -677,13 +641,13 @@ "node": ">=18" }, "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/css-tokenizer": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", - "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", "dev": true, "funding": [ { @@ -701,10 +665,33 @@ "node": ">=18" } }, + "node_modules/@emnapi/core": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.0.tgz", + "integrity": "sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/runtime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.6.0.tgz", - "integrity": "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", + "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -712,9 +699,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", - "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", + "integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==", "cpu": [ "ppc64" ], @@ -729,9 +716,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", - "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz", + "integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==", "cpu": [ "arm" ], @@ -746,9 +733,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", - "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz", + "integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==", "cpu": [ "arm64" ], @@ -763,9 +750,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", - "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz", + "integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==", "cpu": [ "x64" ], @@ -780,9 +767,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", - "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz", + "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==", "cpu": [ "arm64" ], @@ -797,9 +784,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", - "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz", + "integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==", "cpu": [ "x64" ], @@ -814,9 +801,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", - "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz", + "integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==", "cpu": [ "arm64" ], @@ -831,9 +818,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", - "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz", + "integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==", "cpu": [ "x64" ], @@ -848,9 +835,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", - "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz", + "integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==", "cpu": [ "arm" ], @@ -865,9 +852,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", - "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz", + "integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==", "cpu": [ "arm64" ], @@ -882,9 +869,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", - "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz", + "integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==", "cpu": [ "ia32" ], @@ -899,9 +886,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", - "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz", + "integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==", "cpu": [ "loong64" ], @@ -916,9 +903,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", - "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz", + "integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==", "cpu": [ "mips64el" ], @@ -933,9 +920,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", - "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz", + "integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==", "cpu": [ "ppc64" ], @@ -950,9 +937,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", - "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz", + "integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==", "cpu": [ "riscv64" ], @@ -967,9 +954,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", - "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz", + "integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==", "cpu": [ "s390x" ], @@ -984,9 +971,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", - "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz", + "integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==", "cpu": [ "x64" ], @@ -1001,9 +988,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", - "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz", + "integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==", "cpu": [ "arm64" ], @@ -1018,9 +1005,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", - "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz", + "integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==", "cpu": [ "x64" ], @@ -1035,9 +1022,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", - "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz", + "integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==", "cpu": [ "arm64" ], @@ -1052,9 +1039,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", - "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz", + "integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==", "cpu": [ "x64" ], @@ -1068,10 +1055,27 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz", + "integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", - "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz", + "integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==", "cpu": [ "x64" ], @@ -1086,9 +1090,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", - "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz", + "integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==", "cpu": [ "arm64" ], @@ -1103,9 +1107,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", - "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz", + "integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==", "cpu": [ "ia32" ], @@ -1120,9 +1124,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", - "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz", + "integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==", "cpu": [ "x64" ], @@ -1137,7 +1141,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "license": "MIT", "dependencies": { @@ -1155,6 +1161,8 @@ }, "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1166,6 +1174,8 @@ }, "node_modules/@eslint-community/regexpp": { "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, "license": "MIT", "engines": { @@ -1173,11 +1183,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.1", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.5", + "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -1185,8 +1197,23 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.0.tgz", + "integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.16.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/core": { - "version": "0.10.0", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz", + "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1197,7 +1224,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.2.0", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1218,40 +1247,23 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, "node_modules/@eslint/js": { - "version": "9.18.0", + "version": "9.37.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.37.0.tgz", + "integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { - "version": "2.1.5", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1259,11 +1271,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.5", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", + "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.10.0", + "@eslint/core": "^0.16.0", "levn": "^0.4.1" }, "engines": { @@ -1271,31 +1285,31 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.6.9", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", - "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", "license": "MIT", "dependencies": { - "@floating-ui/utils": "^0.2.9" + "@floating-ui/utils": "^0.2.10" } }, "node_modules/@floating-ui/dom": { - "version": "1.6.13", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", - "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.9" + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" } }, "node_modules/@floating-ui/react-dom": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", - "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", + "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", "license": "MIT", "dependencies": { - "@floating-ui/dom": "^1.0.0" + "@floating-ui/dom": "^1.7.4" }, "peerDependencies": { "react": ">=16.8.0", @@ -1303,13 +1317,15 @@ } }, "node_modules/@floating-ui/utils": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", - "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", "license": "MIT" }, "node_modules/@hookform/resolvers": { "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz", + "integrity": "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==", "license": "MIT", "peerDependencies": { "react-hook-form": "^7.0.0" @@ -1317,6 +1333,8 @@ }, "node_modules/@humanfs/core": { "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1324,31 +1342,23 @@ } }, "node_modules/@humanfs/node": { - "version": "0.16.6", + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" + "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1360,7 +1370,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1373,6 +1385,8 @@ }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", "cpu": [ "arm64" ], @@ -1415,6 +1429,8 @@ }, "node_modules/@img/sharp-libvips-darwin-arm64": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", "cpu": [ "arm64" ], @@ -1730,6 +1746,8 @@ }, "node_modules/@isaacs/cliui": { "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "license": "ISC", "dependencies": { "string-width": "^5.1.2", @@ -1744,37 +1762,45 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=6.0.0" + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1892,8 +1918,8 @@ "license": "ISC" }, "node_modules/@nanostores/react": { - "version": "0.8.4", - "resolved": "git+ssh://git@github.com/ai/react.git#34a81026c4aeaa572bc651e3d36b47274de2f890", + "version": "1.0.0", + "resolved": "git+ssh://git@github.com/ai/react.git#a6ad745cc307881916944ee4d6ba04bdda978dc6", "funding": [ { "type": "github", @@ -1902,21 +1928,36 @@ ], "license": "MIT", "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^20.0.0 || >=22.0.0" }, "peerDependencies": { - "nanostores": "^0.9.0 || ^0.10.0 || ^0.11.0", + "nanostores": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^1.0.0", "react": ">=18.0.0" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, "node_modules/@next/env": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.4.tgz", - "integrity": "sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g==", + "version": "15.5.5", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.5.tgz", + "integrity": "sha512-2Zhvss36s/yL+YSxD5ZL5dz5pI6ki1OLxYlh6O77VJ68sBnlUrl5YqhBgCy7FkdMsp9RBeGFwpuDCdpJOqdKeQ==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { "version": "15.1.4", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.1.4.tgz", + "integrity": "sha512-HwlEXwCK3sr6zmVGEvWBjW9tBFs1Oe6hTmTLoFQtpm4As5HCdu8jfSE0XJOp7uhfEGLniIx8yrGxEWwNnY0fmQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2053,6 +2094,8 @@ }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -2064,6 +2107,8 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "license": "MIT", "engines": { "node": ">= 8" @@ -2071,6 +2116,8 @@ }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -2082,12 +2129,23 @@ }, "node_modules/@nolyfill/is-core-module": { "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", "dev": true, "license": "MIT", "engines": { "node": ">=12.4.0" } }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@panva/hkdf": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", @@ -2099,6 +2157,8 @@ }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "license": "MIT", "optional": true, "engines": { @@ -2192,16 +2252,18 @@ "license": "MIT" }, "node_modules/@radix-ui/primitive": { - "version": "1.1.1", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", "license": "MIT" }, "node_modules/@radix-ui/react-arrow": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.1.tgz", - "integrity": "sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.1" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -2219,19 +2281,19 @@ } }, "node_modules/@radix-ui/react-checkbox": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.3.tgz", - "integrity": "sha512-HD7/ocp8f1B3e6OHygH0n7ZKjONkhciy1Nh0yuBgObqThc3oyx+vuMfFHKAknXRHHWVE9XvXStxJFyjUmB8PIw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", + "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-previous": "1.1.0", - "@radix-ui/react-use-size": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2249,17 +2311,19 @@ } }, "node_modules/@radix-ui/react-collapsible": { - "version": "1.1.2", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz", + "integrity": "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2277,15 +2341,15 @@ } }, "node_modules/@radix-ui/react-collection": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.1.tgz", - "integrity": "sha512-LwT3pSho9Dljg+wY2KN2mrrh6y3qELfftINERIzBUO9e0N+t0oMTyn3k9iv+ZqgrwGkRnLpNJrsMv9BZlt2yuA==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-slot": "1.1.1" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -2302,14 +2366,11 @@ } } }, - "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", - "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2320,8 +2381,10 @@ } } }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.1", + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -2333,40 +2396,27 @@ } } }, - "node_modules/@radix-ui/react-context": { - "version": "1.1.1", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.6.tgz", - "integrity": "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.5", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.2", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-portal": "1.1.4", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-slot": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.1.0", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", @@ -2382,39 +2432,32 @@ } } }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.2.tgz", - "integrity": "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==", + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-callback-ref": "1.1.0" - }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.4.tgz", - "integrity": "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==", + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-layout-effect": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2431,13 +2474,19 @@ } } }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", - "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", + "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.1.2" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -2454,28 +2503,10 @@ } } }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", - "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-direction": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", - "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -2487,40 +2518,15 @@ } } }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz", - "integrity": "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-escape-keydown": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-primitive": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", - "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.1.2" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2537,13 +2543,13 @@ } } }, - "node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", - "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2555,19 +2561,13 @@ } } }, - "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.5.tgz", - "integrity": "sha512-50ZmEFL1kOuLalPKHrLWvPFMons2fGx9TqQCWlPwDVpbAnaUJ1g4XNcKqFNMQymYU0kKWR4MDDi+9vUQBGFgcQ==", + "node_modules/@radix-ui/react-label": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz", + "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-menu": "2.1.5", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-controllable-state": "1.1.0" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -2584,26 +2584,67 @@ } } }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.1", - "license": "MIT", + "node_modules/@radix-ui/react-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", + "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-callback-ref": "1.1.0" + "node_modules/@radix-ui/react-popover": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", + "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", @@ -2620,27 +2661,46 @@ } } }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.0", + "node_modules/@radix-ui/react-popper": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.0" + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-label": { - "version": "2.1.1", + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.1" + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2657,30 +2717,14 @@ } } }, - "node_modules/@radix-ui/react-menu": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.5.tgz", - "integrity": "sha512-uH+3w5heoMJtqVCgYOtYVMECk1TOrkUn0OG0p5MqXC0W2ppcuVeESbou8PTHoqAjbdTEK19AGXBWcEtR5WpEQg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-collection": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-dismissable-layer": "1.1.4", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.1", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.1", - "@radix-ui/react-portal": "1.1.3", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-roving-focus": "1.1.1", - "@radix-ui/react-slot": "1.1.1", - "@radix-ui/react-use-callback-ref": "1.1.0", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.2" + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2697,17 +2741,13 @@ } } }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.4.tgz", - "integrity": "sha512-XDUI0IVYVSwjMXxM6P4Dfti7AH+Y4oS/TB+sglZ/EXc7cqLwGAmp1NlMrcUjj7ks6R5WTZuWKv44FBbLpwU3sA==", + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-escape-keydown": "1.1.0" + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -2724,45 +2764,21 @@ } } }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", - "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-popover": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.6.tgz", - "integrity": "sha512-NQouW0x4/GnkFJ/pRqsIS3rM/k97VzKnVb2jB7Gq7VEGPy5g7uNV1ykySFt7eWSp3i2uSGFwaJcvIRJBAHmmFg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.5", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.2", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.2", - "@radix-ui/react-portal": "1.1.4", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-slot": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.1.0", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -2779,13 +2795,13 @@ } } }, - "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-arrow": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.2.tgz", - "integrity": "sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==", + "node_modules/@radix-ui/react-separator": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz", + "integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.2" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -2802,47 +2818,42 @@ } } }, - "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.2.tgz", - "integrity": "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==", + "node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-callback-ref": "1.1.0" + "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-popper": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.2.tgz", - "integrity": "sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==", + "node_modules/@radix-ui/react-tooltip": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", + "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==", "license": "MIT", "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-rect": "1.1.0", - "@radix-ui/react-use-size": "1.1.0", - "@radix-ui/rect": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -2859,60 +2870,47 @@ } } }, - "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-portal": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.4.tgz", - "integrity": "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==", + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-primitive": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", - "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.1.2" + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", - "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2924,110 +2922,79 @@ } } }, - "node_modules/@radix-ui/react-popper": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.1.tgz", - "integrity": "sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==", + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", "license": "MIT", "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-rect": "1.1.0", - "@radix-ui/react-use-size": "1.1.0", - "@radix-ui/rect": "1.1.0" + "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-portal": { - "version": "1.1.3", + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-presence": { - "version": "1.1.2", + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.0.1", + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.1.1" + "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "node_modules/@radix-ui/react-use-size": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", - "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -3039,21 +3006,13 @@ } } }, - "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.1.tgz", - "integrity": "sha512-QE1RoxPGJ/Nm8Qmk0PxP8ojmoaS67i0s7hVssS7KuI2FQoc/uzVlZsqKfQvxPE6D8hICCPHJ4D88zNhT3OOmkw==", + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-collection": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -3070,416 +3029,10 @@ } } }, - "node_modules/@radix-ui/react-separator": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz", - "integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-slot/node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.8.tgz", - "integrity": "sha512-YAA2cu48EkJZdAMHC0dqo9kialOcRStbtiY4nJPaht7Ptrhcvpo+eDChaM6BIs8kL6a8Z5l5poiqLnXcNduOkA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.5", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.2", - "@radix-ui/react-portal": "1.1.4", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-slot": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-visually-hidden": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-arrow": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.2.tgz", - "integrity": "sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.0.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.2.tgz", - "integrity": "sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==", - "license": "MIT", - "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-rect": "1.1.0", - "@radix-ui/react-use-size": "1.1.0", - "@radix-ui/rect": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-portal": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.4.tgz", - "integrity": "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-primitive": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", - "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", - "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.0", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.1.0", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.0", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.0", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-previous": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz", - "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-rect": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", - "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/rect": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-size": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", - "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.2.tgz", - "integrity": "sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.0.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-primitive": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", - "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", - "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/rect": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", - "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", "license": "MIT" }, "node_modules/@redocly/ajv": { @@ -3499,6 +3052,13 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/@redocly/ajv/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/@redocly/config": { "version": "0.22.2", "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.22.2.tgz", @@ -3551,10 +3111,17 @@ "node": ">=10" } }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.32.1.tgz", - "integrity": "sha512-/pqA4DmqyCm8u5YIDzIdlLcEmuvxb0v8fZdFhVMszSpDTgbQKdw3/mB3eMUHIbubtJ6F9j+LtmyCnHTEqIHyzA==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz", + "integrity": "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==", "cpu": [ "arm" ], @@ -3566,9 +3133,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.32.1.tgz", - "integrity": "sha512-If3PDskT77q7zgqVqYuj7WG3WC08G1kwXGVFi9Jr8nY6eHucREHkfpX79c0ACAjLj3QIWKPJR7w4i+f5EdLH5Q==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz", + "integrity": "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==", "cpu": [ "arm64" ], @@ -3580,9 +3147,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.32.1.tgz", - "integrity": "sha512-zCpKHioQ9KgZToFp5Wvz6zaWbMzYQ2LJHQ+QixDKq52KKrF65ueu6Af4hLlLWHjX1Wf/0G5kSJM9PySW9IrvHA==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz", + "integrity": "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==", "cpu": [ "arm64" ], @@ -3594,9 +3161,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.32.1.tgz", - "integrity": "sha512-sFvF+t2+TyUo/ZQqUcifrJIgznx58oFZbdHS9TvHq3xhPVL9nOp+yZ6LKrO9GWTP+6DbFtoyLDbjTpR62Mbr3Q==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz", + "integrity": "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==", "cpu": [ "x64" ], @@ -3608,9 +3175,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.32.1.tgz", - "integrity": "sha512-NbOa+7InvMWRcY9RG+B6kKIMD/FsnQPH0MWUvDlQB1iXnF/UcKSudCXZtv4lW+C276g3w5AxPbfry5rSYvyeYA==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz", + "integrity": "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==", "cpu": [ "arm64" ], @@ -3622,9 +3189,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.32.1.tgz", - "integrity": "sha512-JRBRmwvHPXR881j2xjry8HZ86wIPK2CcDw0EXchE1UgU0ubWp9nvlT7cZYKc6bkypBt745b4bglf3+xJ7hXWWw==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz", + "integrity": "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==", "cpu": [ "x64" ], @@ -3636,9 +3203,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.32.1.tgz", - "integrity": "sha512-PKvszb+9o/vVdUzCCjL0sKHukEQV39tD3fepXxYrHE3sTKrRdCydI7uldRLbjLmDA3TFDmh418XH19NOsDRH8g==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz", + "integrity": "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==", "cpu": [ "arm" ], @@ -3650,9 +3217,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.32.1.tgz", - "integrity": "sha512-9WHEMV6Y89eL606ReYowXuGF1Yb2vwfKWKdD1A5h+OYnPZSJvxbEjxTRKPgi7tkP2DSnW0YLab1ooy+i/FQp/Q==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz", + "integrity": "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==", "cpu": [ "arm" ], @@ -3664,9 +3231,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.32.1.tgz", - "integrity": "sha512-tZWc9iEt5fGJ1CL2LRPw8OttkCBDs+D8D3oEM8mH8S1ICZCtFJhD7DZ3XMGM8kpqHvhGUTvNUYVDnmkj4BDXnw==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz", + "integrity": "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==", "cpu": [ "arm64" ], @@ -3678,9 +3245,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.32.1.tgz", - "integrity": "sha512-FTYc2YoTWUsBz5GTTgGkRYYJ5NGJIi/rCY4oK/I8aKowx1ToXeoVVbIE4LGAjsauvlhjfl0MYacxClLld1VrOw==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz", + "integrity": "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==", "cpu": [ "arm64" ], @@ -3691,10 +3258,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.32.1.tgz", - "integrity": "sha512-F51qLdOtpS6P1zJVRzYM0v6MrBNypyPEN1GfMiz0gPu9jN8ScGaEFIZQwteSsGKg799oR5EaP7+B2jHgL+d+Kw==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz", + "integrity": "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==", "cpu": [ "loong64" ], @@ -3705,10 +3272,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.32.1.tgz", - "integrity": "sha512-wO0WkfSppfX4YFm5KhdCCpnpGbtgQNj/tgvYzrVYFKDpven8w2N6Gg5nB6w+wAMO3AIfSTWeTjfVe+uZ23zAlg==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz", + "integrity": "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==", "cpu": [ "ppc64" ], @@ -3720,9 +3287,23 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.32.1.tgz", - "integrity": "sha512-iWswS9cIXfJO1MFYtI/4jjlrGb/V58oMu4dYJIKnR5UIwbkzR0PJ09O0PDZT0oJ3LYWXBSWahNf/Mjo6i1E5/g==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz", + "integrity": "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz", + "integrity": "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==", "cpu": [ "riscv64" ], @@ -3734,9 +3315,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.32.1.tgz", - "integrity": "sha512-RKt8NI9tebzmEthMnfVgG3i/XeECkMPS+ibVZjZ6mNekpbbUmkNWuIN2yHsb/mBPyZke4nlI4YqIdFPgKuoyQQ==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz", + "integrity": "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==", "cpu": [ "s390x" ], @@ -3748,9 +3329,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.32.1.tgz", - "integrity": "sha512-WQFLZ9c42ECqEjwg/GHHsouij3pzLXkFdz0UxHa/0OM12LzvX7DzedlY0SIEly2v18YZLRhCRoHZDxbBSWoGYg==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz", + "integrity": "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==", "cpu": [ "x64" ], @@ -3762,9 +3343,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.32.1.tgz", - "integrity": "sha512-BLoiyHDOWoS3uccNSADMza6V6vCNiphi94tQlVIL5de+r6r/CCQuNnerf+1g2mnk2b6edp5dk0nhdZ7aEjOBsA==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.4.tgz", + "integrity": "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==", "cpu": [ "x64" ], @@ -3775,10 +3356,24 @@ "linux" ] }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz", + "integrity": "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.32.1.tgz", - "integrity": "sha512-w2l3UnlgYTNNU+Z6wOR8YdaioqfEnwPjIsJ66KxKAf0p+AuL2FHeTX6qvM+p/Ue3XPBVNyVSfCrfZiQh7vZHLQ==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz", + "integrity": "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==", "cpu": [ "arm64" ], @@ -3790,9 +3385,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.32.1.tgz", - "integrity": "sha512-Am9H+TGLomPGkBnaPWie4F3x+yQ2rr4Bk2jpwy+iV+Gel9jLAu/KqT8k3X4jxFPW6Zf8OMnehyutsd+eHoq1WQ==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz", + "integrity": "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==", "cpu": [ "ia32" ], @@ -3803,10 +3398,24 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.4.tgz", + "integrity": "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.32.1.tgz", - "integrity": "sha512-ar80GhdZb4DgmW3myIS9nRFYcpJRSME8iqWgzH2i44u+IdrzmiXVxeFnExQ5v4JYUSpg94bWjevMG8JHf1Da5Q==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz", + "integrity": "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==", "cpu": [ "x64" ], @@ -3819,72 +3428,39 @@ }, "node_modules/@rtsao/scc": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "dev": true, "license": "MIT" }, "node_modules/@rushstack/eslint-patch": { - "version": "1.10.5", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.14.0.tgz", + "integrity": "sha512-WJFej426qe4RWOm9MMtP4V3CV4AucXolQty+GRgAWLgQXmpCuwzs7hEpxxhSc/znXUSxum9d/P/32MW0FlAAlA==", "dev": true, "license": "MIT" }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "license": "MIT" + }, "node_modules/@swc/counter": { "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", "license": "Apache-2.0" }, "node_modules/@swc/helpers": { "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.8.0" } }, - "node_modules/@t3-oss/env-core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@t3-oss/env-core/-/env-core-0.12.0.tgz", - "integrity": "sha512-lOPj8d9nJJTt81mMuN9GMk8x5veOt7q9m11OSnCBJhwp1QrL/qR+M8Y467ULBSm9SunosryWNbmQQbgoiMgcdw==", - "license": "MIT", - "peerDependencies": { - "typescript": ">=5.0.0", - "valibot": "^1.0.0-beta.7 || ^1.0.0", - "zod": "^3.24.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - }, - "valibot": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/@t3-oss/env-nextjs": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@t3-oss/env-nextjs/-/env-nextjs-0.12.0.tgz", - "integrity": "sha512-rFnvYk1049RnNVUPvY8iQ55AuQh1Rr+qZzQBh3t++RttCGK4COpXGNxS4+45afuQq02lu+QAOy/5955aU8hRKw==", - "license": "MIT", - "dependencies": { - "@t3-oss/env-core": "0.12.0" - }, - "peerDependencies": { - "typescript": ">=5.0.0", - "valibot": "^1.0.0-beta.7 || ^1.0.0", - "zod": "^3.24.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - }, - "valibot": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, "node_modules/@tailwindcss/line-clamp": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.4.4.tgz", @@ -3895,37 +3471,21 @@ } }, "node_modules/@tailwindcss/typography": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz", - "integrity": "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==", + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz", + "integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==", "license": "MIT", "dependencies": { - "lodash.castarray": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.merge": "^4.6.2", "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, - "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@tanstack/query-core": { - "version": "5.66.0", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.66.0.tgz", - "integrity": "sha512-J+JeBtthiKxrpzUu7rfIPDzhscXF2p5zE/hVdrqkACBP8Yu0M96mwJ5m/8cPPYQE9aRNvXztXHlNwIh4FEeMZw==", + "version": "5.90.3", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.3.tgz", + "integrity": "sha512-HtPOnCwmx4dd35PfXU8jjkhwYrsHfuqgC8RCJIwWglmhIUIlzPP0ZcEkDAc+UtAWCiLm7T8rxeEfHZlz3hYMCA==", "license": "MIT", "funding": { "type": "github", @@ -3933,12 +3493,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.66.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.66.0.tgz", - "integrity": "sha512-z3sYixFQJe8hndFnXgWu7C79ctL+pI0KAelYyW+khaNJ1m22lWrhJU2QrsTcRKMuVPtoZvfBYrTStIdKo+x0Xw==", + "version": "5.90.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.3.tgz", + "integrity": "sha512-i/LRL6DtuhG6bjGzavIMIVuKKPWx2AnEBIsBfuMm3YoHne0a20nWmsatOCBcVSaT0/8/5YFjNkebHAPLVUSi0Q==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.66.0" + "@tanstack/query-core": "5.90.3" }, "funding": { "type": "github", @@ -3949,9 +3509,9 @@ } }, "node_modules/@testing-library/dom": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", - "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", "peer": true, @@ -3960,38 +3520,27 @@ "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", - "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", + "picocolors": "1.1.1", "pretty-format": "^27.0.2" }, "engines": { "node": ">=18" } }, - "node_modules/@testing-library/dom/node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "dequal": "^2.0.3" - } - }, "node_modules/@testing-library/jest-dom": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz", - "integrity": "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", "dev": true, "license": "MIT", "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", - "chalk": "^3.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", - "lodash": "^4.17.21", + "picocolors": "^1.1.1", "redent": "^3.0.0" }, "engines": { @@ -4000,20 +3549,6 @@ "yarn": ">=1" } }, - "node_modules/@testing-library/jest-dom/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", @@ -4022,9 +3557,9 @@ "license": "MIT" }, "node_modules/@testing-library/react": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.2.0.tgz", - "integrity": "sha512-2cSskAvA1QNtKc8Y9VJQRv0tm3hLVgxRGDB+KYhIaPQJ1I+RHbhIXcM+zClKXzMes/wshsMVzf4B9vS4IZpqDQ==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.0.tgz", + "integrity": "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==", "dev": true, "license": "MIT", "dependencies": { @@ -4134,6 +3669,17 @@ "url": "https://opencollective.com/turf" } }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -4156,9 +3702,9 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, "license": "MIT", "dependencies": { @@ -4177,28 +3723,51 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/chai": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", + "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.20.7" + "@types/deep-eql": "*" } }, "node_modules/@types/debug": { "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", "license": "MIT", "dependencies": { "@types/ms": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { - "version": "1.0.6", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "license": "MIT" }, "node_modules/@types/estree-jsx": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", "license": "MIT", "dependencies": { "@types/estree": "*" @@ -4221,6 +3790,8 @@ }, "node_modules/@types/hast": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", "license": "MIT", "dependencies": { "@types/unist": "*" @@ -4228,16 +3799,22 @@ }, "node_modules/@types/js-cookie": { "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz", + "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==", "dev": true, "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, "license": "MIT" }, "node_modules/@types/json5": { "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true, "license": "MIT" }, @@ -4260,22 +3837,28 @@ }, "node_modules/@types/mdast": { "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", "license": "MIT", "dependencies": { "@types/unist": "*" } }, "node_modules/@types/ms": { - "version": "0.7.34", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "license": "MIT" }, "node_modules/@types/node": { - "version": "20.17.12", + "version": "20.19.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.21.tgz", + "integrity": "sha512-CsGG2P3I5y48RPMfprQGfy4JPRZ6csfC3ltBZSRItG3ngggmNY/qs2uZKp4p9VbrpqNNSMzUZNFZKzgOGnd/VA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.21.0" } }, "node_modules/@types/pbf": { @@ -4285,14 +3868,16 @@ "license": "MIT" }, "node_modules/@types/plotly.js": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/plotly.js/-/plotly.js-3.0.7.tgz", - "integrity": "sha512-oFgNQsBpVOuQ2jYl3qRO9uyuixT+jbeMXGbbAHTV7AM9Bi6N9B2ZFYxO64hBwXu65Khb7w8a6d4EJnrLC30Vlw==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/plotly.js/-/plotly.js-3.0.8.tgz", + "integrity": "sha512-FjmSFaLmHVgBIBL6H0yX5k/AB3a7FQzjKBlRUF8YT6HiXMArE+hbXYIZXZ/42SBrdL05LWEog0zPqEaIDNsAiw==", "dev": true, "license": "MIT" }, "node_modules/@types/react": { - "version": "19.0.6", + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz", + "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "license": "MIT", "peer": true, "dependencies": { @@ -4300,12 +3885,14 @@ } }, "node_modules/@types/react-dom": { - "version": "19.0.3", + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz", + "integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==", "devOptional": true, "license": "MIT", "peer": true, "peerDependencies": { - "@types/react": "^19.0.0" + "@types/react": "^19.2.0" } }, "node_modules/@types/react-plotly.js": { @@ -4330,22 +3917,63 @@ }, "node_modules/@types/unist": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.19.1", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.1.tgz", + "integrity": "sha512-rUsLh8PXmBjdiPY+Emjz9NX2yHvhS11v0SR6xNJkm5GM1MO9ea/1GoDKlHHZGrOJclL/cZ2i/vRUYVtjRhrHVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.46.1", + "@typescript-eslint/type-utils": "8.46.1", + "@typescript-eslint/utils": "8.46.1", + "@typescript-eslint/visitor-keys": "8.46.1", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.46.1", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.1.tgz", + "integrity": "sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.19.1", - "@typescript-eslint/type-utils": "8.19.1", - "@typescript-eslint/utils": "8.19.1", - "@typescript-eslint/visitor-keys": "8.19.1", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.0" + "@typescript-eslint/scope-manager": "8.46.1", + "@typescript-eslint/types": "8.46.1", + "@typescript-eslint/typescript-estree": "8.46.1", + "@typescript-eslint/visitor-keys": "8.46.1", + "debug": "^4.3.4" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4355,21 +3983,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/parser": { - "version": "8.19.1", + "node_modules/@typescript-eslint/project-service": { + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.1.tgz", + "integrity": "sha512-FOIaFVMHzRskXr5J4Jp8lFVV0gz5ngv3RHmn+E4HYxSJ3DgDzU7fVI1/M7Ijh1zf6S7HIoaIOtln1H5y8V+9Zg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.19.1", - "@typescript-eslint/types": "8.19.1", - "@typescript-eslint/typescript-estree": "8.19.1", - "@typescript-eslint/visitor-keys": "8.19.1", + "@typescript-eslint/tsconfig-utils": "^8.46.1", + "@typescript-eslint/types": "^8.46.1", "debug": "^4.3.4" }, "engines": { @@ -4380,35 +4006,56 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.19.1", + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.1.tgz", + "integrity": "sha512-weL9Gg3/5F0pVQKiF8eOXFZp8emqWzZsOJuWRUNtHT+UNV2xSJegmpCNQHy37aEQIbToTq7RHKhWvOsmbM680A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.1", - "@typescript-eslint/visitor-keys": "8.19.1" + "@typescript-eslint/types": "8.46.1", + "@typescript-eslint/visitor-keys": "8.46.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.1.tgz", + "integrity": "sha512-X88+J/CwFvlJB+mK09VFqx5FE4H5cXD+H/Bdza2aEWkSb8hnWIQorNcscRl4IEo1Cz9VI/+/r/jnGWkbWPx54g==", + "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.19.1", + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.1.tgz", + "integrity": "sha512-+BlmiHIiqufBxkVnOtFwjah/vrkF4MtKKvpXrKSPLCkCtAp8H01/VV43sfqA98Od7nJpDcFnkwgyfQbOG0AMvw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.19.1", - "@typescript-eslint/utils": "8.19.1", + "@typescript-eslint/types": "8.46.1", + "@typescript-eslint/typescript-estree": "8.46.1", + "@typescript-eslint/utils": "8.46.1", "debug": "^4.3.4", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4419,11 +4066,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.19.1", + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.1.tgz", + "integrity": "sha512-C+soprGBHwWBdkDpbaRC4paGBrkIXxVlNohadL5o0kfhsXqOC6GYH2S/Obmig+I0HTDl8wMaRySwrfrXVP8/pQ==", "dev": true, "license": "MIT", "engines": { @@ -4435,18 +4084,22 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.19.1", + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.1.tgz", + "integrity": "sha512-uIifjT4s8cQKFQ8ZBXXyoUODtRoAd7F7+G8MKmtzj17+1UbdzFl52AzRyZRyKqPHhgzvXunnSckVu36flGy8cg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.1", - "@typescript-eslint/visitor-keys": "8.19.1", + "@typescript-eslint/project-service": "8.46.1", + "@typescript-eslint/tsconfig-utils": "8.46.1", + "@typescript-eslint/types": "8.46.1", + "@typescript-eslint/visitor-keys": "8.46.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4456,11 +4109,13 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4469,6 +4124,8 @@ }, "node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": { "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", "dependencies": { @@ -4484,6 +4141,8 @@ }, "node_modules/@typescript-eslint/typescript-estree/node_modules/glob-parent": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", "dependencies": { @@ -4495,6 +4154,8 @@ }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { @@ -4507,15 +4168,30 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/utils": { - "version": "8.19.1", + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.1.tgz", + "integrity": "sha512-vkYUy6LdZS7q1v/Gxb2Zs7zziuXN0wxqsetJdeZdRe/f5dwJFglmuvZBfTUivCtjH725C1jWCDfpadadD95EDQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.19.1", - "@typescript-eslint/types": "8.19.1", - "@typescript-eslint/typescript-estree": "8.19.1" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.46.1", + "@typescript-eslint/types": "8.46.1", + "@typescript-eslint/typescript-estree": "8.46.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4526,59 +4202,343 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.19.1", + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.1.tgz", + "integrity": "sha512-ptkmIf2iDkNUjdeu2bQqhFPV1m6qTnFFjg7PPDjxKWaMaP0Z6I9l30Jr3g5QqbZGdw8YdYvLp+XnqnWWZOg/NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.46.1", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.19.1", - "eslint-visitor-keys": "^4.2.0" - }, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vercel/oidc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.0.2.tgz", + "integrity": "sha512-JekxQ0RApo4gS4un/iMGsIL1/k4KUBe3HmnGcDvzHuFBdQdudEJgTqcsJC7y6Ul4Yw5CeykgvQbX2XeEJd0+DA==", + "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">= 20" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.1", - "license": "ISC" - }, "node_modules/@vitejs/plugin-react": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", - "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.26.0", - "@babel/plugin-transform-react-jsx-self": "^7.25.9", - "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", - "react-refresh": "^0.14.2" + "react-refresh": "^0.17.0" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/@vitest/expect": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.5.tgz", - "integrity": "sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.0.5", - "@vitest/utils": "3.0.5", - "chai": "^5.1.2", + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, "funding": { @@ -4586,13 +4546,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.5.tgz", - "integrity": "sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.0.5", + "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -4601,7 +4561,7 @@ }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0" + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "peerDependenciesMeta": { "msw": { @@ -4613,9 +4573,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.5.tgz", - "integrity": "sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", "dev": true, "license": "MIT", "dependencies": { @@ -4626,56 +4586,57 @@ } }, "node_modules/@vitest/runner": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.5.tgz", - "integrity": "sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.0.5", - "pathe": "^2.0.2" + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.5.tgz", - "integrity": "sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.0.5", + "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", - "pathe": "^2.0.2" + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.5.tgz", - "integrity": "sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", "dev": true, "license": "MIT", "dependencies": { - "tinyspy": "^3.0.2" + "tinyspy": "^4.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.5.tgz", - "integrity": "sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.0.5", - "loupe": "^3.1.2", + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" }, "funding": { @@ -4689,7 +4650,9 @@ "license": "MIT" }, "node_modules/acorn": { - "version": "8.14.0", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", "peer": true, @@ -4702,6 +4665,8 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -4709,15 +4674,50 @@ } }, "node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, "license": "MIT", "engines": { "node": ">= 14" } }, + "node_modules/ai": { + "version": "5.0.72", + "resolved": "https://registry.npmjs.org/ai/-/ai-5.0.72.tgz", + "integrity": "sha512-LB4APrlESLGHG/5x+VVdl0yYPpHPHpnGd5Gwl7AWVL+n7T0GYsNos/S/6dZ5CZzxLnPPEBkRgvJC4rupeZqyNg==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/gateway": "1.0.40", + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.12", + "@opentelemetry/api": "1.9.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -4729,17 +4729,18 @@ } }, "node_modules/ansi-regex": { - "version": "6.1.0", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": ">=8" } }, "node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -4753,10 +4754,14 @@ }, "node_modules/any-promise": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", "license": "MIT" }, "node_modules/anymatch": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", @@ -4768,15 +4773,21 @@ }, "node_modules/arg": { "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", "license": "MIT" }, "node_modules/argparse": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, "license": "Python-2.0" }, "node_modules/aria-hidden": { - "version": "1.2.4", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", "license": "MIT", "dependencies": { "tslib": "^2.0.0" @@ -4786,11 +4797,13 @@ } }, "node_modules/aria-query": { - "version": "5.3.2", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" + "dependencies": { + "dequal": "^2.0.3" } }, "node_modules/array-bounds": { @@ -4801,6 +4814,8 @@ }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, "license": "MIT", "dependencies": { @@ -4824,16 +4839,20 @@ } }, "node_modules/array-includes": { - "version": "3.1.8", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4865,6 +4884,8 @@ }, "node_modules/array.prototype.findlast": { "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4883,16 +4904,19 @@ } }, "node_modules/array.prototype.findlastindex": { - "version": "1.2.5", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4903,6 +4927,8 @@ }, "node_modules/array.prototype.flat": { "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "dev": true, "license": "MIT", "dependencies": { @@ -4920,6 +4946,8 @@ }, "node_modules/array.prototype.flatmap": { "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, "license": "MIT", "dependencies": { @@ -4937,6 +4965,8 @@ }, "node_modules/array.prototype.tosorted": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, "license": "MIT", "dependencies": { @@ -4952,6 +4982,8 @@ }, "node_modules/arraybuffer.prototype.slice": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4982,18 +5014,25 @@ }, "node_modules/ast-types-flow": { "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "dev": true, "license": "MIT" }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, "node_modules/available-typed-arrays": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5007,7 +5046,9 @@ } }, "node_modules/axe-core": { - "version": "4.10.2", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.0.tgz", + "integrity": "sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==", "dev": true, "license": "MPL-2.0", "engines": { @@ -5016,6 +5057,8 @@ }, "node_modules/axobject-query": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5024,6 +5067,8 @@ }, "node_modules/bail": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", "license": "MIT", "funding": { "type": "github", @@ -5032,6 +5077,8 @@ }, "node_modules/balanced-match": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, "node_modules/base64-arraybuffer": { @@ -5063,8 +5110,20 @@ ], "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.16", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.16.tgz", + "integrity": "sha512-OMu3BGQ4E7P1ErFsIPpbJh0qvDudM/UuJeHgkAvfWe+0HFJCXh+t/l8L6fVLR55RI/UbKrVLnAXZSVwd9ysWYw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "license": "MIT", "engines": { "node": ">=8" @@ -5102,7 +5161,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -5112,6 +5173,8 @@ }, "node_modules/braces": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -5121,9 +5184,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.26.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz", + "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==", "dev": true, "funding": [ { @@ -5142,10 +5205,11 @@ "license": "MIT", "peer": true, "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "baseline-browser-mapping": "^2.8.9", + "caniuse-lite": "^1.0.30001746", + "electron-to-chromium": "^1.5.227", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -5186,6 +5250,8 @@ }, "node_modules/busboy": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", "dependencies": { "streamsearch": "^1.1.0" }, @@ -5205,6 +5271,8 @@ }, "node_modules/call-bind": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dev": true, "license": "MIT", "dependencies": { @@ -5221,7 +5289,9 @@ } }, "node_modules/call-bind-apply-helpers": { - "version": "1.0.1", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5233,12 +5303,14 @@ } }, "node_modules/call-bound": { - "version": "1.0.3", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "get-intrinsic": "^1.2.6" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -5249,6 +5321,8 @@ }, "node_modules/callsites": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", "engines": { @@ -5257,13 +5331,17 @@ }, "node_modules/camelcase-css": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001692", + "version": "1.0.30001751", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz", + "integrity": "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==", "funding": [ { "type": "opencollective", @@ -5291,6 +5369,8 @@ }, "node_modules/ccount": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", "license": "MIT", "funding": { "type": "github", @@ -5298,9 +5378,9 @@ } }, "node_modules/chai": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", - "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", "dev": true, "license": "MIT", "dependencies": { @@ -5311,11 +5391,13 @@ "pathval": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -5338,6 +5420,8 @@ }, "node_modules/character-entities": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", "license": "MIT", "funding": { "type": "github", @@ -5346,6 +5430,8 @@ }, "node_modules/character-entities-html4": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", "license": "MIT", "funding": { "type": "github", @@ -5354,6 +5440,8 @@ }, "node_modules/character-entities-legacy": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", "license": "MIT", "funding": { "type": "github", @@ -5362,6 +5450,8 @@ }, "node_modules/character-reference-invalid": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", "license": "MIT", "funding": { "type": "github", @@ -5369,9 +5459,9 @@ } }, "node_modules/chart.js": { - "version": "4.4.7", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.7.tgz", - "integrity": "sha512-pwkcKfdzTMAU/+jNosKhNL2bHtJc/sSmYgVbuGTEDhzkrhmyihmP7vUc/5ZK9WopidMDHNe3Wm7jOd/WhuHWuw==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", + "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==", "license": "MIT", "peer": true, "dependencies": { @@ -5393,6 +5483,8 @@ }, "node_modules/chokidar": { "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "license": "MIT", "dependencies": { "anymatch": "~3.1.2", @@ -5415,6 +5507,8 @@ }, "node_modules/chokidar/node_modules/glob-parent": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -5431,6 +5525,8 @@ }, "node_modules/class-variance-authority": { "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", "license": "Apache-2.0", "dependencies": { "clsx": "^2.1.1" @@ -5441,25 +5537,29 @@ }, "node_modules/client-only": { "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, "node_modules/clsx": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/cmdk": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.0.4.tgz", - "integrity": "sha512-AnsjfHyHpQ/EFeAnG216WY7A5LiYCoZzCSygiLvfXC3H3LFGCprErteUcszaVluGOhuOTbJS3jWHrSDYPBBygg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", "license": "MIT", "dependencies": { - "@radix-ui/react-dialog": "^1.1.2", + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-id": "^1.1.0", - "@radix-ui/react-primitive": "^2.0.0", - "use-sync-external-store": "^1.2.2" + "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", @@ -5468,6 +5568,8 @@ }, "node_modules/color": { "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", "license": "MIT", "optional": true, "dependencies": { @@ -5498,6 +5600,8 @@ }, "node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -5517,6 +5621,8 @@ }, "node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/color-normalize": { @@ -5576,6 +5682,8 @@ }, "node_modules/color-string": { "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", "license": "MIT", "optional": true, "dependencies": { @@ -5590,21 +5698,10 @@ "dev": true, "license": "MIT" }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/comma-separated-tokens": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", "license": "MIT", "funding": { "type": "github", @@ -5613,6 +5710,8 @@ }, "node_modules/commander": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "license": "MIT", "engines": { "node": ">= 6" @@ -5620,6 +5719,8 @@ }, "node_modules/concat-map": { "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, "license": "MIT" }, @@ -5668,6 +5769,8 @@ }, "node_modules/cross-spawn": { "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -5746,6 +5849,8 @@ }, "node_modules/cssesc": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "license": "MIT", "bin": { "cssesc": "bin/cssesc" @@ -5755,13 +5860,13 @@ } }, "node_modules/cssstyle": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.2.1.tgz", - "integrity": "sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", "dev": true, "license": "MIT", "dependencies": { - "@asamuzakjp/css-color": "^2.8.2", + "@asamuzakjp/css-color": "^3.2.0", "rrweb-cssom": "^0.8.0" }, "engines": { @@ -5770,6 +5875,8 @@ }, "node_modules/csstype": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, "node_modules/d": { @@ -5926,6 +6033,8 @@ }, "node_modules/damerau-levenshtein": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true, "license": "BSD-2-Clause" }, @@ -5945,6 +6054,8 @@ }, "node_modules/data-view-buffer": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5961,6 +6072,8 @@ }, "node_modules/data-view-byte-length": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5977,6 +6090,8 @@ }, "node_modules/data-view-byte-offset": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5992,7 +6107,9 @@ } }, "node_modules/debug": { - "version": "4.4.0", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -6007,14 +6124,16 @@ } }, "node_modules/decimal.js": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", - "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", "dev": true, "license": "MIT" }, "node_modules/decode-named-character-reference": { - "version": "1.0.2", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", "license": "MIT", "dependencies": { "character-entities": "^2.0.0" @@ -6036,11 +6155,15 @@ }, "node_modules/deep-is": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, "license": "MIT" }, "node_modules/define-data-property": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, "license": "MIT", "dependencies": { @@ -6057,6 +6180,8 @@ }, "node_modules/define-properties": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "license": "MIT", "dependencies": { @@ -6080,18 +6205,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/dequal": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "license": "MIT", "engines": { "node": ">=6" @@ -6104,7 +6221,9 @@ "license": "MIT" }, "node_modules/detect-libc": { - "version": "2.0.3", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "license": "Apache-2.0", "optional": true, "engines": { @@ -6113,10 +6232,14 @@ }, "node_modules/detect-node-es": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", "license": "MIT" }, "node_modules/devlop": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", "license": "MIT", "dependencies": { "dequal": "^2.0.0" @@ -6128,14 +6251,20 @@ }, "node_modules/didyoumean": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", "license": "Apache-2.0" }, "node_modules/dlv": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", "license": "MIT" }, "node_modules/doctrine": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -6173,6 +6302,8 @@ }, "node_modules/dunder-proto": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dev": true, "license": "MIT", "dependencies": { @@ -6210,12 +6341,14 @@ }, "node_modules/eastasianwidth": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.88", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.88.tgz", - "integrity": "sha512-K3C2qf1o+bGzbilTDCTBhTQcMS9KW60yTAaTeeXsfvQuTDDwlokLam/AdqlqcSy9u4UainDgsHV23ksXAOgamw==", + "version": "1.5.237", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.237.tgz", + "integrity": "sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg==", "dev": true, "license": "ISC" }, @@ -6236,6 +6369,8 @@ }, "node_modules/emoji-regex": { "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "license": "MIT" }, "node_modules/end-of-stream": { @@ -6247,22 +6382,10 @@ "once": "^1.4.0" } }, - "node_modules/enhanced-resolve": { - "version": "5.18.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -6273,7 +6396,9 @@ } }, "node_modules/es-abstract": { - "version": "1.23.9", + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", "dev": true, "license": "MIT", "dependencies": { @@ -6281,18 +6406,18 @@ "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.3", + "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.0", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", @@ -6304,21 +6429,24 @@ "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", + "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.0", + "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.3", + "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.3", + "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", @@ -6327,7 +6455,7 @@ "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.18" + "which-typed-array": "^1.1.19" }, "engines": { "node": ">= 0.4" @@ -6338,6 +6466,8 @@ }, "node_modules/es-define-property": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, "license": "MIT", "engines": { @@ -6346,6 +6476,8 @@ }, "node_modules/es-errors": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, "license": "MIT", "engines": { @@ -6354,6 +6486,8 @@ }, "node_modules/es-iterator-helpers": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", "dev": true, "license": "MIT", "dependencies": { @@ -6379,14 +6513,16 @@ } }, "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true, "license": "MIT" }, "node_modules/es-object-atoms": { - "version": "1.0.0", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dev": true, "license": "MIT", "dependencies": { @@ -6398,6 +6534,8 @@ }, "node_modules/es-set-tostringtag": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, "license": "MIT", "dependencies": { @@ -6411,15 +6549,22 @@ } }, "node_modules/es-shim-unscopables": { - "version": "1.0.2", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/es-to-primitive": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, "license": "MIT", "dependencies": { @@ -6487,9 +6632,9 @@ } }, "node_modules/esbuild": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", - "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz", + "integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -6500,31 +6645,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.24.2", - "@esbuild/android-arm": "0.24.2", - "@esbuild/android-arm64": "0.24.2", - "@esbuild/android-x64": "0.24.2", - "@esbuild/darwin-arm64": "0.24.2", - "@esbuild/darwin-x64": "0.24.2", - "@esbuild/freebsd-arm64": "0.24.2", - "@esbuild/freebsd-x64": "0.24.2", - "@esbuild/linux-arm": "0.24.2", - "@esbuild/linux-arm64": "0.24.2", - "@esbuild/linux-ia32": "0.24.2", - "@esbuild/linux-loong64": "0.24.2", - "@esbuild/linux-mips64el": "0.24.2", - "@esbuild/linux-ppc64": "0.24.2", - "@esbuild/linux-riscv64": "0.24.2", - "@esbuild/linux-s390x": "0.24.2", - "@esbuild/linux-x64": "0.24.2", - "@esbuild/netbsd-arm64": "0.24.2", - "@esbuild/netbsd-x64": "0.24.2", - "@esbuild/openbsd-arm64": "0.24.2", - "@esbuild/openbsd-x64": "0.24.2", - "@esbuild/sunos-x64": "0.24.2", - "@esbuild/win32-arm64": "0.24.2", - "@esbuild/win32-ia32": "0.24.2", - "@esbuild/win32-x64": "0.24.2" + "@esbuild/aix-ppc64": "0.25.11", + "@esbuild/android-arm": "0.25.11", + "@esbuild/android-arm64": "0.25.11", + "@esbuild/android-x64": "0.25.11", + "@esbuild/darwin-arm64": "0.25.11", + "@esbuild/darwin-x64": "0.25.11", + "@esbuild/freebsd-arm64": "0.25.11", + "@esbuild/freebsd-x64": "0.25.11", + "@esbuild/linux-arm": "0.25.11", + "@esbuild/linux-arm64": "0.25.11", + "@esbuild/linux-ia32": "0.25.11", + "@esbuild/linux-loong64": "0.25.11", + "@esbuild/linux-mips64el": "0.25.11", + "@esbuild/linux-ppc64": "0.25.11", + "@esbuild/linux-riscv64": "0.25.11", + "@esbuild/linux-s390x": "0.25.11", + "@esbuild/linux-x64": "0.25.11", + "@esbuild/netbsd-arm64": "0.25.11", + "@esbuild/netbsd-x64": "0.25.11", + "@esbuild/openbsd-arm64": "0.25.11", + "@esbuild/openbsd-x64": "0.25.11", + "@esbuild/openharmony-arm64": "0.25.11", + "@esbuild/sunos-x64": "0.25.11", + "@esbuild/win32-arm64": "0.25.11", + "@esbuild/win32-ia32": "0.25.11", + "@esbuild/win32-x64": "0.25.11" } }, "node_modules/escalade": { @@ -6539,6 +6685,8 @@ }, "node_modules/escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", "engines": { @@ -6570,21 +6718,24 @@ } }, "node_modules/eslint": { - "version": "9.18.0", + "version": "9.37.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz", + "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.10.0", - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.18.0", - "@eslint/plugin-kit": "^0.2.5", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.4.0", + "@eslint/core": "^0.16.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.37.0", + "@eslint/plugin-kit": "^0.4.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.1", + "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", @@ -6592,9 +6743,9 @@ "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.2.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -6630,6 +6781,8 @@ }, "node_modules/eslint-config-next": { "version": "15.1.4", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.1.4.tgz", + "integrity": "sha512-u9+7lFmfhKNgGjhQ9tBeyCFsPJyq0SvGioMJBngPC7HXUpR0U+ckEwQR48s7TrRNHra1REm6evGL2ie38agALg==", "dev": true, "license": "MIT", "dependencies": { @@ -6656,6 +6809,8 @@ }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "license": "MIT", "dependencies": { @@ -6666,6 +6821,8 @@ }, "node_modules/eslint-import-resolver-node/node_modules/debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6673,24 +6830,25 @@ } }, "node_modules/eslint-import-resolver-typescript": { - "version": "3.7.0", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", "dev": true, "license": "ISC", "dependencies": { "@nolyfill/is-core-module": "1.0.39", - "debug": "^4.3.7", - "enhanced-resolve": "^5.15.0", - "fast-glob": "^3.3.2", - "get-tsconfig": "^4.7.5", - "is-bun-module": "^1.0.2", - "is-glob": "^4.0.3", - "stable-hash": "^0.0.4" + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + "url": "https://opencollective.com/eslint-import-resolver-typescript" }, "peerDependencies": { "eslint": "*", @@ -6706,34 +6864,10 @@ } } }, - "node_modules/eslint-import-resolver-typescript/node_modules/fast-glob": { - "version": "3.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/eslint-import-resolver-typescript/node_modules/glob-parent": { - "version": "5.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/eslint-module-utils": { - "version": "2.12.0", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", "dev": true, "license": "MIT", "dependencies": { @@ -6750,6 +6884,8 @@ }, "node_modules/eslint-module-utils/node_modules/debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6757,29 +6893,31 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.31.0", + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.8", - "array.prototype.findlastindex": "^1.2.5", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.0", + "eslint-module-utils": "^2.12.1", "hasown": "^2.0.2", - "is-core-module": "^2.15.1", + "is-core-module": "^2.16.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", - "object.values": "^1.2.0", + "object.values": "^1.2.1", "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.8", + "string.prototype.trimend": "^1.0.9", "tsconfig-paths": "^3.15.0" }, "engines": { @@ -6791,22 +6929,18 @@ }, "node_modules/eslint-plugin-import/node_modules/debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/eslint-plugin-jsx-a11y": { "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -6833,8 +6967,20 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eslint-plugin-react": { - "version": "7.37.4", + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, "license": "MIT", "dependencies": { @@ -6848,7 +6994,7 @@ "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", - "object.entries": "^1.1.8", + "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", @@ -6865,7 +7011,9 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "5.1.0", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", "dev": true, "license": "MIT", "engines": { @@ -6877,6 +7025,8 @@ }, "node_modules/eslint-plugin-react/node_modules/resolve": { "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, "license": "MIT", "dependencies": { @@ -6891,16 +7041,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/eslint-scope": { - "version": "8.2.0", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -6915,7 +7059,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.0", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -6925,30 +7071,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, "node_modules/esniff": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", @@ -6965,13 +7087,15 @@ } }, "node_modules/espree": { - "version": "10.3.0", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6995,6 +7119,8 @@ }, "node_modules/esquery": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -7006,6 +7132,8 @@ }, "node_modules/esrecurse": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -7017,6 +7145,8 @@ }, "node_modules/estraverse": { "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -7024,6 +7154,8 @@ }, "node_modules/estree-util-is-identifier-name": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", "license": "MIT", "funding": { "type": "opencollective", @@ -7042,6 +7174,8 @@ }, "node_modules/esutils": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" @@ -7066,10 +7200,19 @@ "node": ">=0.8.x" } }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/expect-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", - "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -7087,6 +7230,8 @@ }, "node_modules/extend": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, "node_modules/falafel": { @@ -7116,11 +7261,15 @@ }, "node_modules/fast-deep-equal": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, "license": "MIT" }, "node_modules/fast-glob": { "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, "license": "MIT", "dependencies": { @@ -7136,6 +7285,8 @@ }, "node_modules/fast-glob/node_modules/glob-parent": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", "dependencies": { @@ -7163,11 +7314,15 @@ }, "node_modules/fast-levenshtein": { "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, "license": "MIT" }, "node_modules/fastq": { - "version": "1.18.0", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -7175,6 +7330,8 @@ }, "node_modules/file-entry-cache": { "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7186,6 +7343,8 @@ }, "node_modules/fill-range": { "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -7196,6 +7355,8 @@ }, "node_modules/find-up": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { @@ -7211,6 +7372,8 @@ }, "node_modules/flat-cache": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", "dependencies": { @@ -7222,7 +7385,9 @@ } }, "node_modules/flatted": { - "version": "3.3.2", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, "license": "ISC" }, @@ -7254,18 +7419,28 @@ } }, "node_modules/for-each": { - "version": "0.3.3", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/foreground-child": { - "version": "3.3.0", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -7275,21 +7450,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/from2": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", @@ -7302,6 +7462,9 @@ }, "node_modules/fsevents": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, "license": "MIT", "optional": true, "os": [ @@ -7313,6 +7476,8 @@ }, "node_modules/function-bind": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7320,6 +7485,8 @@ }, "node_modules/function.prototype.name": { "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, "license": "MIT", "dependencies": { @@ -7339,12 +7506,24 @@ }, "node_modules/functions-have-names": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -7368,16 +7547,18 @@ "license": "MIT" }, "node_modules/get-intrinsic": { - "version": "1.2.7", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", + "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "get-proto": "^1.0.0", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", @@ -7392,6 +7573,8 @@ }, "node_modules/get-nonce": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", "license": "MIT", "engines": { "node": ">=6" @@ -7399,6 +7582,8 @@ }, "node_modules/get-proto": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dev": true, "license": "MIT", "dependencies": { @@ -7423,6 +7608,8 @@ }, "node_modules/get-symbol-description": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, "license": "MIT", "dependencies": { @@ -7438,7 +7625,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.8.1", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.12.0.tgz", + "integrity": "sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==", "dev": true, "license": "MIT", "dependencies": { @@ -7520,6 +7709,8 @@ }, "node_modules/glob": { "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -7538,6 +7729,8 @@ }, "node_modules/glob-parent": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "license": "ISC", "dependencies": { "is-glob": "^4.0.3" @@ -7547,7 +7740,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.1", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -7555,6 +7750,8 @@ }, "node_modules/glob/node_modules/minimatch": { "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -7606,6 +7803,8 @@ }, "node_modules/globals": { "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, "license": "MIT", "engines": { @@ -7617,6 +7816,8 @@ }, "node_modules/globalthis": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7833,6 +8034,8 @@ }, "node_modules/gopd": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, "license": "MIT", "engines": { @@ -7844,10 +8047,14 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, "node_modules/graphemer": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true, "license": "MIT" }, @@ -7859,6 +8066,8 @@ }, "node_modules/has-bigints": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, "license": "MIT", "engines": { @@ -7870,6 +8079,8 @@ }, "node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { @@ -7896,6 +8107,8 @@ }, "node_modules/has-property-descriptors": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "license": "MIT", "dependencies": { @@ -7907,6 +8120,8 @@ }, "node_modules/has-proto": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7921,6 +8136,8 @@ }, "node_modules/has-symbols": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, "license": "MIT", "engines": { @@ -7932,6 +8149,8 @@ }, "node_modules/has-tostringtag": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "license": "MIT", "dependencies": { @@ -7946,6 +8165,8 @@ }, "node_modules/hasown": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -7955,7 +8176,9 @@ } }, "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.2", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", "license": "MIT", "dependencies": { "@types/estree": "^1.0.0", @@ -7968,9 +8191,9 @@ "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^6.0.0", + "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", - "style-to-object": "^1.0.0", + "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" }, @@ -7981,6 +8204,8 @@ }, "node_modules/hast-util-whitespace": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0" @@ -8005,6 +8230,8 @@ }, "node_modules/html-url-attributes": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", "license": "MIT", "funding": { "type": "opencollective", @@ -8074,6 +8301,8 @@ }, "node_modules/ignore": { "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { @@ -8081,7 +8310,9 @@ } }, "node_modules/import-fresh": { - "version": "3.3.0", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8097,6 +8328,8 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", "engines": { @@ -8114,9 +8347,9 @@ } }, "node_modules/index-to-position": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.1.0.tgz", - "integrity": "sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", "dev": true, "license": "MIT", "engines": { @@ -8143,10 +8376,14 @@ }, "node_modules/inline-style-parser": { "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", "license": "MIT" }, "node_modules/internal-slot": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, "license": "MIT", "dependencies": { @@ -8160,6 +8397,8 @@ }, "node_modules/is-alphabetical": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", "license": "MIT", "funding": { "type": "github", @@ -8168,6 +8407,8 @@ }, "node_modules/is-alphanumerical": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", "license": "MIT", "dependencies": { "is-alphabetical": "^2.0.0", @@ -8180,6 +8421,8 @@ }, "node_modules/is-array-buffer": { "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, "license": "MIT", "dependencies": { @@ -8195,15 +8438,20 @@ } }, "node_modules/is-arrayish": { - "version": "0.3.2", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", "license": "MIT", "optional": true }, "node_modules/is-async-function": { - "version": "2.1.0", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, "license": "MIT", "dependencies": { + "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", @@ -8218,6 +8466,8 @@ }, "node_modules/is-bigint": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8232,6 +8482,8 @@ }, "node_modules/is-binary-path": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" @@ -8241,11 +8493,13 @@ } }, "node_modules/is-boolean-object": { - "version": "1.2.1", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", + "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" }, "engines": { @@ -8262,15 +8516,32 @@ "license": "MIT" }, "node_modules/is-bun-module": { - "version": "1.3.0", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", "dev": true, "license": "MIT", "dependencies": { - "semver": "^7.6.3" + "semver": "^7.7.1" + } + }, + "node_modules/is-bun-module/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/is-callable": { "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, "license": "MIT", "engines": { @@ -8282,6 +8553,8 @@ }, "node_modules/is-core-module": { "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -8295,6 +8568,8 @@ }, "node_modules/is-data-view": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, "license": "MIT", "dependencies": { @@ -8311,6 +8586,8 @@ }, "node_modules/is-date-object": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, "license": "MIT", "dependencies": { @@ -8326,6 +8603,8 @@ }, "node_modules/is-decimal": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", "license": "MIT", "funding": { "type": "github", @@ -8334,6 +8613,8 @@ }, "node_modules/is-extglob": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -8341,6 +8622,8 @@ }, "node_modules/is-finalizationregistry": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, "license": "MIT", "dependencies": { @@ -8376,18 +8659,23 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-generator-function": { - "version": "1.1.0", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" }, @@ -8400,6 +8688,8 @@ }, "node_modules/is-glob": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -8410,6 +8700,8 @@ }, "node_modules/is-hexadecimal": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", "license": "MIT", "funding": { "type": "github", @@ -8427,6 +8719,8 @@ }, "node_modules/is-map": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, "license": "MIT", "engines": { @@ -8442,8 +8736,23 @@ "integrity": "sha512-mlcHZA84t1qLSuWkt2v0I2l61PYdyQDt4aG1mLIXF5FDMm4+haBCxCPYSr/uwqQNRk1MiTizn0ypEuRAOLRAew==", "license": "MIT" }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "license": "MIT", "engines": { "node": ">=0.12.0" @@ -8451,6 +8760,8 @@ }, "node_modules/is-number-object": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, "license": "MIT", "dependencies": { @@ -8475,6 +8786,8 @@ }, "node_modules/is-plain-obj": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "license": "MIT", "engines": { "node": ">=12" @@ -8492,6 +8805,8 @@ }, "node_modules/is-regex": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, "license": "MIT", "dependencies": { @@ -8509,6 +8824,8 @@ }, "node_modules/is-set": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, "license": "MIT", "engines": { @@ -8520,6 +8837,8 @@ }, "node_modules/is-shared-array-buffer": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, "license": "MIT", "dependencies": { @@ -8534,6 +8853,8 @@ }, "node_modules/is-string": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, "license": "MIT", "dependencies": { @@ -8561,6 +8882,8 @@ }, "node_modules/is-symbol": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, "license": "MIT", "dependencies": { @@ -8577,6 +8900,8 @@ }, "node_modules/is-typed-array": { "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8591,6 +8916,8 @@ }, "node_modules/is-weakmap": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, "license": "MIT", "engines": { @@ -8601,11 +8928,13 @@ } }, "node_modules/is-weakref": { - "version": "1.1.0", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -8616,6 +8945,8 @@ }, "node_modules/is-weakset": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8631,14 +8962,20 @@ }, "node_modules/isarray": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, "node_modules/iterator.prototype": { "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, "license": "MIT", "dependencies": { @@ -8655,6 +8992,8 @@ }, "node_modules/jackspeak": { "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -8668,6 +9007,8 @@ }, "node_modules/jiti": { "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "license": "MIT", "peer": true, "bin": { @@ -8685,6 +9026,8 @@ }, "node_modules/js-cookie": { "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", "license": "MIT", "engines": { "node": ">=14" @@ -8708,10 +9051,14 @@ }, "node_modules/js-tokens": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "license": "MIT", "dependencies": { @@ -8722,17 +9069,16 @@ } }, "node_modules/jsdom": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.0.0.tgz", - "integrity": "sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==", + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", - "decimal.js": "^10.4.3", - "form-data": "^4.0.1", + "decimal.js": "^10.5.0", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", @@ -8742,12 +9088,12 @@ "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^5.0.0", + "tough-cookie": "^5.1.1", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.1.0", + "whatwg-url": "^14.1.1", "ws": "^8.18.0", "xml-name-validator": "^5.0.0" }, @@ -8778,13 +9124,15 @@ }, "node_modules/json-buffer": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, "license": "MIT" }, "node_modules/json-edit-react": { - "version": "1.26.2", - "resolved": "https://registry.npmjs.org/json-edit-react/-/json-edit-react-1.26.2.tgz", - "integrity": "sha512-/8a8je+Wm5/vgrhPJubhjbOoRPzzjPp8dLom4/DSIcRE1cJlgishGriuc//3o0FI+0VhsPYmAIxkvxNpUU6Ykg==", + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/json-edit-react/-/json-edit-react-1.29.0.tgz", + "integrity": "sha512-qxho/m0w6GCaKLic6XsZgmCrKKwluMvoy92teRBRus1PRHJQiP4pkFQJ90lmrno1KNgSePYIG0mcscS0GjdH3A==", "license": "MIT", "dependencies": { "object-property-assigner": "^1.3.5", @@ -8801,14 +9149,16 @@ "license": "(AFL-2.1 OR BSD-3-Clause)" }, "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, "license": "MIT" }, @@ -8819,18 +9169,22 @@ "license": "MIT" }, "node_modules/json5": { - "version": "1.0.2", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, "bin": { "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, "node_modules/jsx-ast-utils": { "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8851,6 +9205,8 @@ }, "node_modules/keyv": { "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", "dependencies": { @@ -8868,11 +9224,15 @@ }, "node_modules/language-subtag-registry": { "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", "dev": true, "license": "CC0-1.0" }, "node_modules/language-tags": { "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "dev": true, "license": "MIT", "dependencies": { @@ -8884,6 +9244,8 @@ }, "node_modules/levn": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8896,6 +9258,8 @@ }, "node_modules/lilconfig": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "license": "MIT", "engines": { "node": ">=14" @@ -8906,10 +9270,14 @@ }, "node_modules/lines-and-columns": { "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "license": "MIT" }, "node_modules/locate-path": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { @@ -8922,31 +9290,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.castarray": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", - "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "license": "MIT" }, "node_modules/longest-streak": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", "license": "MIT", "funding": { "type": "github", @@ -8955,6 +9308,8 @@ }, "node_modules/loose-envify": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -8964,18 +9319,26 @@ } }, "node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", "dev": true, "license": "MIT" }, "node_modules/lru-cache": { - "version": "10.4.3", - "license": "ISC" + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } }, "node_modules/lucide-react": { - "version": "0.471.1", + "version": "0.471.2", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.471.2.tgz", + "integrity": "sha512-A8fDycQxGeaSOTaI7Bm4fg8LBXO7Qr9ORAX47bDRvugCsjLIliugQO0PkKFoeAD57LIQwlWKd3NIQ3J7hYp84g==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -8992,13 +9355,13 @@ } }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/map-limit": { @@ -9156,9 +9519,9 @@ } }, "node_modules/marked": { - "version": "15.0.7", - "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.7.tgz", - "integrity": "sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg==", + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -9169,6 +9532,8 @@ }, "node_modules/math-intrinsics": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, "license": "MIT", "engines": { @@ -9214,6 +9579,8 @@ }, "node_modules/mdast-util-from-markdown": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -9235,9 +9602,9 @@ } }, "node_modules/mdast-util-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz", - "integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", "license": "MIT", "dependencies": { "mdast-util-from-markdown": "^2.0.0", @@ -9271,9 +9638,9 @@ } }, "node_modules/mdast-util-gfm-footnote": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz", - "integrity": "sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -9337,6 +9704,8 @@ }, "node_modules/mdast-util-mdx-expression": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", "license": "MIT", "dependencies": { "@types/estree-jsx": "^1.0.0", @@ -9353,6 +9722,8 @@ }, "node_modules/mdast-util-mdx-jsx": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", "license": "MIT", "dependencies": { "@types/estree-jsx": "^1.0.0", @@ -9375,6 +9746,8 @@ }, "node_modules/mdast-util-mdxjs-esm": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", "license": "MIT", "dependencies": { "@types/estree-jsx": "^1.0.0", @@ -9391,6 +9764,8 @@ }, "node_modules/mdast-util-phrasing": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -9403,6 +9778,8 @@ }, "node_modules/mdast-util-to-hast": { "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -9422,6 +9799,8 @@ }, "node_modules/mdast-util-to-markdown": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -9441,6 +9820,8 @@ }, "node_modules/mdast-util-to-string": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0" @@ -9452,13 +9833,17 @@ }, "node_modules/merge2": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/micromark": { - "version": "4.0.1", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", "funding": [ { "type": "GitHub Sponsors", @@ -9491,7 +9876,9 @@ } }, "node_modules/micromark-core-commonmark": { - "version": "2.0.2", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", "funding": [ { "type": "GitHub Sponsors", @@ -9645,6 +10032,8 @@ }, "node_modules/micromark-factory-destination": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", "funding": [ { "type": "GitHub Sponsors", @@ -9664,6 +10053,8 @@ }, "node_modules/micromark-factory-label": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", "funding": [ { "type": "GitHub Sponsors", @@ -9684,6 +10075,8 @@ }, "node_modules/micromark-factory-space": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", "funding": [ { "type": "GitHub Sponsors", @@ -9702,6 +10095,8 @@ }, "node_modules/micromark-factory-title": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", "funding": [ { "type": "GitHub Sponsors", @@ -9722,6 +10117,8 @@ }, "node_modules/micromark-factory-whitespace": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", "funding": [ { "type": "GitHub Sponsors", @@ -9742,6 +10139,8 @@ }, "node_modules/micromark-util-character": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", "funding": [ { "type": "GitHub Sponsors", @@ -9760,6 +10159,8 @@ }, "node_modules/micromark-util-chunked": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", "funding": [ { "type": "GitHub Sponsors", @@ -9777,6 +10178,8 @@ }, "node_modules/micromark-util-classify-character": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", "funding": [ { "type": "GitHub Sponsors", @@ -9796,6 +10199,8 @@ }, "node_modules/micromark-util-combine-extensions": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", "funding": [ { "type": "GitHub Sponsors", @@ -9814,6 +10219,8 @@ }, "node_modules/micromark-util-decode-numeric-character-reference": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", "funding": [ { "type": "GitHub Sponsors", @@ -9831,6 +10238,8 @@ }, "node_modules/micromark-util-decode-string": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", "funding": [ { "type": "GitHub Sponsors", @@ -9851,6 +10260,8 @@ }, "node_modules/micromark-util-encode": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", "funding": [ { "type": "GitHub Sponsors", @@ -9865,6 +10276,8 @@ }, "node_modules/micromark-util-html-tag-name": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", "funding": [ { "type": "GitHub Sponsors", @@ -9879,6 +10292,8 @@ }, "node_modules/micromark-util-normalize-identifier": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", "funding": [ { "type": "GitHub Sponsors", @@ -9896,6 +10311,8 @@ }, "node_modules/micromark-util-resolve-all": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", "funding": [ { "type": "GitHub Sponsors", @@ -9913,6 +10330,8 @@ }, "node_modules/micromark-util-sanitize-uri": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", "funding": [ { "type": "GitHub Sponsors", @@ -9931,7 +10350,9 @@ } }, "node_modules/micromark-util-subtokenize": { - "version": "2.0.3", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", "funding": [ { "type": "GitHub Sponsors", @@ -9952,6 +10373,8 @@ }, "node_modules/micromark-util-symbol": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", "funding": [ { "type": "GitHub Sponsors", @@ -9965,7 +10388,9 @@ "license": "MIT" }, "node_modules/micromark-util-types": { - "version": "2.0.1", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", "funding": [ { "type": "GitHub Sponsors", @@ -9980,6 +10405,8 @@ }, "node_modules/micromatch": { "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -9989,29 +10416,6 @@ "node": ">=8.6" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -10024,6 +10428,8 @@ }, "node_modules/minimatch": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { @@ -10035,6 +10441,8 @@ }, "node_modules/minimist": { "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -10042,6 +10450,8 @@ }, "node_modules/minipass": { "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -10081,6 +10491,8 @@ }, "node_modules/ms": { "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/murmurhash-js": { @@ -10091,6 +10503,8 @@ }, "node_modules/mz": { "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", "license": "MIT", "dependencies": { "any-promise": "^1.0.0", @@ -10099,7 +10513,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.8", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "funding": [ { "type": "github", @@ -10115,7 +10531,9 @@ } }, "node_modules/nanostores": { - "version": "0.11.3", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/nanostores/-/nanostores-1.0.1.tgz", + "integrity": "sha512-kNZ9xnoJYKg/AfxjrVL4SS0fKX++4awQReGqWnwTRHxeHGZ1FJFVgTqr/eMrNQdp0Tz7M7tG/TDaX8QfHDwVCw==", "funding": [ { "type": "github", @@ -10125,7 +10543,23 @@ "license": "MIT", "peer": true, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^20.0.0 || >=22.0.0" + } + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" } }, "node_modules/native-promise-only": { @@ -10136,6 +10570,8 @@ }, "node_modules/natural-compare": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, "license": "MIT" }, @@ -10280,8 +10716,16 @@ "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", "license": "ISC" }, + "node_modules/next/node_modules/@next/env": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.4.tgz", + "integrity": "sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g==", + "license": "MIT" + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "funding": [ { "type": "opencollective", @@ -10307,14 +10751,16 @@ } }, "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "version": "2.0.25", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.25.tgz", + "integrity": "sha512-4auku8B/vw5psvTiiN9j1dAOsXvMoGqJuKJcR+dTdqiXEK20mMTk1UEo3HS16LeGQsVG6+qKTPM9u/qQ2LqATA==", "dev": true, "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10339,9 +10785,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.16", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz", - "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==", + "version": "2.2.22", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.22.tgz", + "integrity": "sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==", "dev": true, "license": "MIT" }, @@ -10353,20 +10799,26 @@ }, "node_modules/object-assign": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/object-hash": { - "version": "3.0.0", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/object-inspect": { - "version": "1.13.3", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, "license": "MIT", "engines": { @@ -10378,6 +10830,8 @@ }, "node_modules/object-keys": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, "license": "MIT", "engines": { @@ -10398,6 +10852,8 @@ }, "node_modules/object.assign": { "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, "license": "MIT", "dependencies": { @@ -10416,13 +10872,16 @@ } }, "node_modules/object.entries": { - "version": "1.1.8", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "es-object-atoms": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -10430,6 +10889,8 @@ }, "node_modules/object.fromentries": { "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10447,6 +10908,8 @@ }, "node_modules/object.groupby": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10460,6 +10923,8 @@ }, "node_modules/object.values": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, "license": "MIT", "dependencies": { @@ -10476,9 +10941,9 @@ } }, "node_modules/oidc-token-hash": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz", - "integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.1.tgz", + "integrity": "sha512-D7EmwxJV6DsEB6vOFLrBM2OzsVgQzgPWyHlV2OOAVj772n+WTXpudC9e9u5BVKQnYwaD30Ivhi9b+4UeBcGu9g==", "license": "MIT", "engines": { "node": "^10.13.0 || >=12.0.0" @@ -10494,9 +10959,9 @@ } }, "node_modules/openapi-typescript": { - "version": "7.9.1", - "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.9.1.tgz", - "integrity": "sha512-9gJtoY04mk6iPMbToPjPxEAtfXZ0dTsMZtsgUI8YZta0btPPig9DJFP4jlerQD/7QOwYgb0tl+zLUpDf7vb7VA==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.10.1.tgz", + "integrity": "sha512-rBcU8bjKGGZQT4K2ekSTY2Q5veOQbVG/lTKZ49DeCyT9z62hM2Vj/LLHjDHC9W7LJG8YMHcdXpRZDqC1ojB/lw==", "dev": true, "license": "MIT", "dependencies": { @@ -10504,7 +10969,7 @@ "ansi-colors": "^4.1.3", "change-case": "^5.4.4", "parse-json": "^8.3.0", - "supports-color": "^10.1.0", + "supports-color": "^10.2.2", "yargs-parser": "^21.1.1" }, "bin": { @@ -10554,15 +11019,6 @@ "node": ">=10" } }, - "node_modules/openid-client/node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/openid-client/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -10571,6 +11027,8 @@ }, "node_modules/optionator": { "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", "dependencies": { @@ -10587,6 +11045,8 @@ }, "node_modules/own-keys": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", "dev": true, "license": "MIT", "dependencies": { @@ -10603,6 +11063,8 @@ }, "node_modules/p-limit": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10617,6 +11079,8 @@ }, "node_modules/p-locate": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { @@ -10631,10 +11095,14 @@ }, "node_modules/package-json-from-dist": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "license": "BlueOak-1.0.0" }, "node_modules/parent-module": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", "dependencies": { @@ -10652,6 +11120,8 @@ }, "node_modules/parse-entities": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", @@ -10669,6 +11139,8 @@ }, "node_modules/parse-entities/node_modules/@types/unist": { "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", "license": "MIT" }, "node_modules/parse-json": { @@ -10711,13 +11183,13 @@ "license": "MIT" }, "node_modules/parse5": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", - "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", "dev": true, "license": "MIT", "dependencies": { - "entities": "^4.5.0" + "entities": "^6.0.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" @@ -10725,6 +11197,8 @@ }, "node_modules/path-exists": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", "engines": { @@ -10733,6 +11207,8 @@ }, "node_modules/path-key": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "license": "MIT", "engines": { "node": ">=8" @@ -10740,10 +11216,14 @@ }, "node_modules/path-parse": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "license": "MIT" }, "node_modules/path-scurry": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", @@ -10756,17 +11236,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/pathe": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.2.tgz", - "integrity": "sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", "dev": true, "license": "MIT", "engines": { @@ -10800,10 +11286,14 @@ }, "node_modules/picocolors": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -10814,23 +11304,28 @@ }, "node_modules/pify": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/pirates": { - "version": "4.0.6", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/plotly.js": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-3.1.1.tgz", - "integrity": "sha512-s4XPAXAZajmdpHoyPOyeL6jwPHW+tZtmbVBii9IDJbzbn7Jkp2Y9dAivJPhmh4djnWSgNE6zmd5e+Jw1f+DvBQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-3.2.0.tgz", + "integrity": "sha512-VH4FRnWbO35rU3IEPrnZ4pxnjS5NbNz6Jk3Rp06jTIleYdAWCdyRrULSOqQB9UMm9XHRKaIKAZPYBOLqQ1svbA==", "license": "MIT", + "peer": true, "dependencies": { "@plotly/d3": "3.8.2", "@plotly/d3-sankey": "0.7.2", @@ -10887,12 +11382,6 @@ "node": ">=18.0.0" } }, - "node_modules/plotly.js-dist-min": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/plotly.js-dist-min/-/plotly.js-dist-min-3.1.1.tgz", - "integrity": "sha512-eyuiESylUXW4kaF+v9J2gy9eZ+YT2uSVLILM4w1Afxnuv9u4UX9OnZnHR1OdF9ybq4x7+9chAzWUUbQ6HvBb3g==", - "license": "MIT" - }, "node_modules/pluralize": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", @@ -10916,7 +11405,9 @@ "license": "MIT" }, "node_modules/possible-typed-array-names": { - "version": "1.0.0", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, "license": "MIT", "engines": { @@ -10924,7 +11415,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "funding": [ { "type": "opencollective", @@ -10942,7 +11435,7 @@ "license": "MIT", "peer": true, "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -10952,6 +11445,8 @@ }, "node_modules/postcss-import": { "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", "license": "MIT", "dependencies": { "postcss-value-parser": "^4.0.0", @@ -10966,7 +11461,19 @@ } }, "node_modules/postcss-js": { - "version": "4.0.1", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { "camelcase-css": "^2.0.1" @@ -10974,16 +11481,14 @@ "engines": { "node": "^12 || ^14 || >= 16" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, "peerDependencies": { "postcss": "^8.4.21" } }, "node_modules/postcss-load-config": { - "version": "4.0.2", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", "funding": [ { "type": "opencollective", @@ -10996,27 +11501,36 @@ ], "license": "MIT", "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" + "lilconfig": "^3.1.1" }, "engines": { - "node": ">= 14" + "node": ">= 18" }, "peerDependencies": { + "jiti": ">=1.21.0", "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { + "jiti": { + "optional": true + }, "postcss": { "optional": true }, - "ts-node": { + "tsx": { + "optional": true + }, + "yaml": { "optional": true } } }, "node_modules/postcss-nested": { "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", "funding": [ { "type": "opencollective", @@ -11038,8 +11552,23 @@ "postcss": "^8.2.14" } }, - "node_modules/postcss-selector-parser": { + "node_modules/postcss-nested/node_modules/postcss-selector-parser": { "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -11051,6 +11580,8 @@ }, "node_modules/postcss-value-parser": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "license": "MIT" }, "node_modules/potpack": { @@ -11060,9 +11591,9 @@ "license": "ISC" }, "node_modules/preact": { - "version": "10.25.4", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.25.4.tgz", - "integrity": "sha512-jLdZDb+Q+odkHJ+MpW/9U5cODzqnB+fy2EiHSZES7ldV5LK7yjlVzTp7R8Xy6W6y75kfK8iWYtFVH7lvjwrCMA==", + "version": "10.27.2", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.27.2.tgz", + "integrity": "sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==", "license": "MIT", "peer": true, "funding": { @@ -11090,6 +11621,8 @@ }, "node_modules/prelude-ls": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", "engines": { @@ -11097,9 +11630,9 @@ } }, "node_modules/prettier": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", "peer": true, @@ -11114,9 +11647,9 @@ } }, "node_modules/prettier-plugin-tailwindcss": { - "version": "0.6.11", - "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.11.tgz", - "integrity": "sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA==", + "version": "0.6.14", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.14.tgz", + "integrity": "sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==", "dev": true, "license": "MIT", "engines": { @@ -11124,6 +11657,8 @@ }, "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-hermes": "*", + "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", @@ -11145,6 +11680,12 @@ "@ianvs/prettier-plugin-sort-imports": { "optional": true }, + "@prettier/plugin-hermes": { + "optional": true + }, + "@prettier/plugin-oxc": { + "optional": true + }, "@prettier/plugin-pug": { "optional": true }, @@ -11201,20 +11742,10 @@ "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", + "react-is": "^17.0.1" + }, "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, "node_modules/pretty-format/node_modules/ansi-styles": { @@ -11230,13 +11761,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true, - "license": "MIT" - }, "node_modules/probe-image-size": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/probe-image-size/-/probe-image-size-7.2.3.tgz", @@ -11256,6 +11780,8 @@ }, "node_modules/prop-types": { "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -11263,8 +11789,16 @@ "react-is": "^16.13.1" } }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, "node_modules/property-information": { - "version": "6.5.0", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", "license": "MIT", "funding": { "type": "github", @@ -11279,6 +11813,8 @@ }, "node_modules/punycode": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", "engines": { @@ -11287,6 +11823,8 @@ }, "node_modules/queue-microtask": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "funding": [ { "type": "github", @@ -11325,9 +11863,10 @@ "license": "MIT" }, "node_modules/react": { - "version": "19.0.0", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", + "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -11343,18 +11882,22 @@ } }, "node_modules/react-dom": { - "version": "19.0.0", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", + "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", "peer": true, "dependencies": { - "scheduler": "^0.25.0" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.0.0" + "react": "^19.2.0" } }, "node_modules/react-hook-form": { - "version": "7.54.2", + "version": "7.65.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.65.0.tgz", + "integrity": "sha512-xtOzDz063WcXvGWaHgLNrNzlsdFgtUWcb32E6WFaGTd7kPZG3EeDusjdZfUsPwKCKVXy1ZlntifaHZ4l8pAsmw==", "license": "MIT", "peer": true, "engines": { @@ -11369,14 +11912,20 @@ } }, "node_modules/react-is": { - "version": "16.13.1", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, "license": "MIT" }, "node_modules/react-markdown": { - "version": "9.0.3", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.1.0.tgz", + "integrity": "sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", @@ -11410,9 +11959,9 @@ } }, "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", "dev": true, "license": "MIT", "engines": { @@ -11420,9 +11969,9 @@ } }, "node_modules/react-remove-scroll": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", - "integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", + "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", "license": "MIT", "dependencies": { "react-remove-scroll-bar": "^2.3.7", @@ -11446,6 +11995,8 @@ }, "node_modules/react-remove-scroll-bar": { "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", "license": "MIT", "dependencies": { "react-style-singleton": "^2.2.2", @@ -11466,6 +12017,8 @@ }, "node_modules/react-style-singleton": { "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", "license": "MIT", "dependencies": { "get-nonce": "^1.0.0", @@ -11485,9 +12038,9 @@ } }, "node_modules/react-textarea-autosize": { - "version": "8.5.7", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.7.tgz", - "integrity": "sha512-2MqJ3p0Jh69yt9ktFIaZmORHXw4c4bxSIhCeWiFwmJ9EYKgLmuNII3e9c9b2UO+ijl4StnpZdqpxNIhTdHvqtQ==", + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz", + "integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.20.13", @@ -11503,6 +12056,8 @@ }, "node_modules/read-cache": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", "license": "MIT", "dependencies": { "pify": "^2.3.0" @@ -11537,6 +12092,8 @@ }, "node_modules/readdirp": { "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "license": "MIT", "dependencies": { "picomatch": "^2.2.1" @@ -11561,6 +12118,8 @@ }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, "license": "MIT", "dependencies": { @@ -11580,14 +12139,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "license": "MIT" - }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, "license": "MIT", "dependencies": { @@ -11704,9 +12259,9 @@ } }, "node_modules/remark-gfm": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz", - "integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -11723,6 +12278,8 @@ }, "node_modules/remark-parse": { "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -11736,7 +12293,9 @@ } }, "node_modules/remark-rehype": { - "version": "11.1.1", + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -11777,6 +12336,8 @@ }, "node_modules/resolve": { "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "license": "MIT", "dependencies": { "is-core-module": "^2.16.0", @@ -11795,6 +12356,8 @@ }, "node_modules/resolve-from": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", "engines": { @@ -11803,6 +12366,8 @@ }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, "license": "MIT", "funding": { @@ -11819,7 +12384,9 @@ } }, "node_modules/reusify": { - "version": "1.0.4", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -11833,13 +12400,13 @@ "license": "MIT" }, "node_modules/rollup": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.32.1.tgz", - "integrity": "sha512-z+aeEsOeEa3mEbS1Tjl6sAZ8NE3+AalQz1RJGj81M+fizusbdDMoEJwdJNHfaB40Scr4qNu+welOfes7maKonA==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz", + "integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -11849,25 +12416,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.32.1", - "@rollup/rollup-android-arm64": "4.32.1", - "@rollup/rollup-darwin-arm64": "4.32.1", - "@rollup/rollup-darwin-x64": "4.32.1", - "@rollup/rollup-freebsd-arm64": "4.32.1", - "@rollup/rollup-freebsd-x64": "4.32.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.32.1", - "@rollup/rollup-linux-arm-musleabihf": "4.32.1", - "@rollup/rollup-linux-arm64-gnu": "4.32.1", - "@rollup/rollup-linux-arm64-musl": "4.32.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.32.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.32.1", - "@rollup/rollup-linux-riscv64-gnu": "4.32.1", - "@rollup/rollup-linux-s390x-gnu": "4.32.1", - "@rollup/rollup-linux-x64-gnu": "4.32.1", - "@rollup/rollup-linux-x64-musl": "4.32.1", - "@rollup/rollup-win32-arm64-msvc": "4.32.1", - "@rollup/rollup-win32-ia32-msvc": "4.32.1", - "@rollup/rollup-win32-x64-msvc": "4.32.1", + "@rollup/rollup-android-arm-eabi": "4.52.4", + "@rollup/rollup-android-arm64": "4.52.4", + "@rollup/rollup-darwin-arm64": "4.52.4", + "@rollup/rollup-darwin-x64": "4.52.4", + "@rollup/rollup-freebsd-arm64": "4.52.4", + "@rollup/rollup-freebsd-x64": "4.52.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.4", + "@rollup/rollup-linux-arm-musleabihf": "4.52.4", + "@rollup/rollup-linux-arm64-gnu": "4.52.4", + "@rollup/rollup-linux-arm64-musl": "4.52.4", + "@rollup/rollup-linux-loong64-gnu": "4.52.4", + "@rollup/rollup-linux-ppc64-gnu": "4.52.4", + "@rollup/rollup-linux-riscv64-gnu": "4.52.4", + "@rollup/rollup-linux-riscv64-musl": "4.52.4", + "@rollup/rollup-linux-s390x-gnu": "4.52.4", + "@rollup/rollup-linux-x64-gnu": "4.52.4", + "@rollup/rollup-linux-x64-musl": "4.52.4", + "@rollup/rollup-openharmony-arm64": "4.52.4", + "@rollup/rollup-win32-arm64-msvc": "4.52.4", + "@rollup/rollup-win32-ia32-msvc": "4.52.4", + "@rollup/rollup-win32-x64-gnu": "4.52.4", + "@rollup/rollup-win32-x64-msvc": "4.52.4", "fsevents": "~2.3.2" } }, @@ -11880,6 +12450,8 @@ }, "node_modules/run-parallel": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "funding": [ { "type": "github", @@ -11907,6 +12479,8 @@ }, "node_modules/safe-array-concat": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, "license": "MIT", "dependencies": { @@ -11945,6 +12519,8 @@ }, "node_modules/safe-push-apply": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, "license": "MIT", "dependencies": { @@ -11960,6 +12536,8 @@ }, "node_modules/safe-regex-test": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, "license": "MIT", "dependencies": { @@ -11981,10 +12559,10 @@ "license": "MIT" }, "node_modules/sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", - "license": "ISC" + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz", + "integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==", + "license": "BlueOak-1.0.0" }, "node_modules/saxes": { "version": "6.0.0", @@ -12000,28 +12578,25 @@ } }, "node_modules/scheduler": { - "version": "0.25.0", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, - "node_modules/secure-json-parse": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", - "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", - "license": "BSD-3-Clause" - }, "node_modules/semver": { - "version": "7.6.3", - "devOptional": true, + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" } }, "node_modules/set-function-length": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, "license": "MIT", "dependencies": { @@ -12038,6 +12613,8 @@ }, "node_modules/set-function-name": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, "license": "MIT", "dependencies": { @@ -12052,6 +12629,8 @@ }, "node_modules/set-proto": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", "dev": true, "license": "MIT", "dependencies": { @@ -12071,6 +12650,8 @@ }, "node_modules/sharp": { "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", "hasInstallScript": true, "license": "Apache-2.0", "optional": true, @@ -12107,8 +12688,23 @@ "@img/sharp-win32-x64": "0.33.5" } }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -12119,6 +12715,8 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "license": "MIT", "engines": { "node": ">=8" @@ -12126,6 +12724,8 @@ }, "node_modules/side-channel": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, "license": "MIT", "dependencies": { @@ -12144,6 +12744,8 @@ }, "node_modules/side-channel-list": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "dev": true, "license": "MIT", "dependencies": { @@ -12159,6 +12761,8 @@ }, "node_modules/side-channel-map": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, "license": "MIT", "dependencies": { @@ -12176,6 +12780,8 @@ }, "node_modules/side-channel-weakmap": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, "license": "MIT", "dependencies": { @@ -12201,6 +12807,8 @@ }, "node_modules/signal-exit": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "license": "ISC", "engines": { "node": ">=14" @@ -12216,7 +12824,9 @@ "license": "MIT" }, "node_modules/simple-swizzle": { - "version": "0.2.2", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", "license": "MIT", "optional": true, "dependencies": { @@ -12224,9 +12834,9 @@ } }, "node_modules/sonner": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.1.tgz", - "integrity": "sha512-FRBphaehZ5tLdLcQ8g2WOIRE+Y7BCfWi5Zyd8bCvBjiW8TxxAyoWZIxS661Yz6TGPqFQ4VLzOF89WEYhfynSFQ==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz", + "integrity": "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==", "license": "MIT", "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", @@ -12245,6 +12855,8 @@ }, "node_modules/source-map-js": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -12252,6 +12864,8 @@ }, "node_modules/space-separated-tokens": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", "license": "MIT", "funding": { "type": "github", @@ -12259,7 +12873,9 @@ } }, "node_modules/stable-hash": { - "version": "0.0.4", + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", "dev": true, "license": "MIT" }, @@ -12288,12 +12904,26 @@ } }, "node_modules/std-env": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", - "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", "dev": true, "license": "MIT" }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/stream-parser": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/stream-parser/-/stream-parser-0.3.1.tgz", @@ -12326,6 +12956,8 @@ }, "node_modules/streamsearch": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", "engines": { "node": ">=10.0.0" } @@ -12356,6 +12988,8 @@ }, "node_modules/string-width": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", @@ -12372,6 +13006,8 @@ "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -12382,19 +13018,16 @@ "node": ">=8" } }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, "node_modules/string-width-cjs/node_modules/strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -12405,6 +13038,8 @@ }, "node_modules/string.prototype.includes": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", "dev": true, "license": "MIT", "dependencies": { @@ -12418,6 +13053,8 @@ }, "node_modules/string.prototype.matchall": { "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, "license": "MIT", "dependencies": { @@ -12444,6 +13081,8 @@ }, "node_modules/string.prototype.repeat": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", "dev": true, "license": "MIT", "dependencies": { @@ -12453,6 +13092,8 @@ }, "node_modules/string.prototype.trim": { "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, "license": "MIT", "dependencies": { @@ -12473,6 +13114,8 @@ }, "node_modules/string.prototype.trimend": { "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, "license": "MIT", "dependencies": { @@ -12490,6 +13133,8 @@ }, "node_modules/string.prototype.trimstart": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "license": "MIT", "dependencies": { @@ -12506,6 +13151,8 @@ }, "node_modules/stringify-entities": { "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", "license": "MIT", "dependencies": { "character-entities-html4": "^2.0.0", @@ -12517,7 +13164,9 @@ } }, "node_modules/strip-ansi": { - "version": "7.1.0", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -12532,6 +13181,8 @@ "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -12540,15 +13191,22 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", + "node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/strip-bom": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "license": "MIT", "engines": { @@ -12570,6 +13228,8 @@ }, "node_modules/strip-json-comments": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", "engines": { @@ -12579,14 +13239,45 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/strongly-connected-components": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strongly-connected-components/-/strongly-connected-components-1.0.1.tgz", "integrity": "sha512-i0TFx4wPcO0FwX+4RkLJi1MxmcTv90jNZgxMu9XRnMXMeFUY1VJlIoXpZunPUvUUqbCT1pg5PEkFqqpcaElNaA==", "license": "MIT" }, + "node_modules/style-to-js": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.18.tgz", + "integrity": "sha512-JFPn62D4kJaPTnhFUI244MThx+FEGbi+9dw1b9yBBQ+1CZpV7QAT8kUtJ7b7EUNdHajjF/0x8fT+16oLJoojLg==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.11" + } + }, "node_modules/style-to-object": { - "version": "1.0.8", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.11.tgz", + "integrity": "sha512-5A560JmXr7wDyGLK12Nq/EYS38VkGlglVzkis1JEdbGWSnbQIEhZzTJhzURXN5/8WwwFCs/f/VVcmkTppbXLow==", "license": "MIT", "dependencies": { "inline-style-parser": "0.2.4" @@ -12594,6 +13285,8 @@ }, "node_modules/styled-jsx": { "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", "license": "MIT", "dependencies": { "client-only": "0.0.1" @@ -12615,6 +13308,8 @@ }, "node_modules/sucrase": { "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", @@ -12656,6 +13351,8 @@ }, "node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { @@ -12667,6 +13364,8 @@ }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -12716,9 +13415,9 @@ } }, "node_modules/swr": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.3.tgz", - "integrity": "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.6.tgz", + "integrity": "sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw==", "license": "MIT", "dependencies": { "dequal": "^2.0.3", @@ -12737,6 +13436,8 @@ }, "node_modules/tailwind-merge": { "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", + "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==", "license": "MIT", "funding": { "type": "github", @@ -12744,7 +13445,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.17", + "version": "3.4.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.18.tgz", + "integrity": "sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==", "license": "MIT", "peer": true, "dependencies": { @@ -12756,7 +13459,7 @@ "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.21.6", + "jiti": "^1.21.7", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", @@ -12765,7 +13468,7 @@ "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", @@ -12781,6 +13484,8 @@ }, "node_modules/tailwindcss-animate": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", "license": "MIT", "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders" @@ -12788,6 +13493,8 @@ }, "node_modules/tailwindcss/node_modules/fast-glob": { "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -12802,6 +13509,8 @@ }, "node_modules/tailwindcss/node_modules/fast-glob/node_modules/glob-parent": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -12810,16 +13519,32 @@ "node": ">= 6" } }, - "node_modules/tapable": { - "version": "2.2.1", - "dev": true, + "node_modules/tailwindcss/node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 6" + } + }, + "node_modules/tailwindcss/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" } }, "node_modules/thenify": { "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", "license": "MIT", "dependencies": { "any-promise": "^1.0.0" @@ -12827,6 +13552,8 @@ }, "node_modules/thenify-all": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", "license": "MIT", "dependencies": { "thenify": ">= 3.1.0 < 4" @@ -12877,10 +13604,59 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tinypool": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", - "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", "dev": true, "license": "MIT", "engines": { @@ -12904,9 +13680,9 @@ } }, "node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", "dev": true, "license": "MIT", "engines": { @@ -12914,22 +13690,22 @@ } }, "node_modules/tldts": { - "version": "6.1.75", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.75.tgz", - "integrity": "sha512-+lFzEXhpl7JXgWYaXcB6DqTYXbUArvrWAE/5ioq/X3CdWLbDjpPP4XTrQBmEJ91y3xbe4Fkw7Lxv4P3GWeJaNg==", + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", "dev": true, "license": "MIT", "dependencies": { - "tldts-core": "^6.1.75" + "tldts-core": "^6.1.86" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.75", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.75.tgz", - "integrity": "sha512-AOvV5YYIAFFBfransBzSTyztkc3IMfz5Eq3YluaRiEu55nn43Fzaufx70UqEKYr8BoLCach4q8g/bg6e5+/aFw==", + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", "dev": true, "license": "MIT" }, @@ -12950,6 +13726,8 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -12979,9 +13757,9 @@ "license": "MIT" }, "node_modules/tough-cookie": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.0.tgz", - "integrity": "sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -12992,9 +13770,9 @@ } }, "node_modules/tr46": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", - "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", "dev": true, "license": "MIT", "dependencies": { @@ -13006,6 +13784,8 @@ }, "node_modules/trim-lines": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", "license": "MIT", "funding": { "type": "github", @@ -13014,6 +13794,8 @@ }, "node_modules/trough": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", "license": "MIT", "funding": { "type": "github", @@ -13021,7 +13803,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.0.0", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "dev": true, "license": "MIT", "engines": { @@ -13033,10 +13817,14 @@ }, "node_modules/ts-interface-checker": { "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", "license": "Apache-2.0" }, "node_modules/tsconfig-paths": { "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, "license": "MIT", "dependencies": { @@ -13046,8 +13834,23 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/tslib": { "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, "node_modules/type": { @@ -13058,6 +13861,8 @@ }, "node_modules/type-check": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", "dependencies": { @@ -13082,6 +13887,8 @@ }, "node_modules/typed-array-buffer": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, "license": "MIT", "dependencies": { @@ -13095,6 +13902,8 @@ }, "node_modules/typed-array-byte-length": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, "license": "MIT", "dependencies": { @@ -13113,6 +13922,8 @@ }, "node_modules/typed-array-byte-offset": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, "license": "MIT", "dependencies": { @@ -13133,6 +13944,8 @@ }, "node_modules/typed-array-length": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, "license": "MIT", "dependencies": { @@ -13167,8 +13980,10 @@ } }, "node_modules/typescript": { - "version": "5.7.3", - "devOptional": true, + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, "license": "Apache-2.0", "peer": true, "bin": { @@ -13181,6 +13996,8 @@ }, "node_modules/unbox-primitive": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, "license": "MIT", "dependencies": { @@ -13197,12 +14014,16 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, "node_modules/unified": { "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -13220,6 +14041,8 @@ }, "node_modules/unist-util-is": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" @@ -13231,6 +14054,8 @@ }, "node_modules/unist-util-position": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" @@ -13242,6 +14067,8 @@ }, "node_modules/unist-util-stringify-position": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" @@ -13253,6 +14080,8 @@ }, "node_modules/unist-util-visit": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -13266,6 +14095,8 @@ }, "node_modules/unist-util-visit-parents": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -13282,10 +14113,45 @@ "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==", "license": "MIT" }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, "node_modules/update-browserslist-db": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", - "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dev": true, "funding": [ { @@ -13338,6 +14204,8 @@ }, "node_modules/use-callback-ref": { "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", "license": "MIT", "dependencies": { "tslib": "^2.0.0" @@ -13370,9 +14238,9 @@ } }, "node_modules/use-isomorphic-layout-effect": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.0.tgz", - "integrity": "sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", + "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -13402,6 +14270,8 @@ }, "node_modules/use-sidecar": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", "license": "MIT", "dependencies": { "detect-node-es": "^1.1.0", @@ -13421,7 +14291,9 @@ } }, "node_modules/use-sync-external-store": { - "version": "1.4.0", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -13429,6 +14301,8 @@ }, "node_modules/util-deprecate": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, "node_modules/uuid": { @@ -13442,6 +14316,8 @@ }, "node_modules/vfile": { "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -13453,7 +14329,9 @@ } }, "node_modules/vfile-message": { - "version": "4.0.2", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -13465,22 +14343,25 @@ } }, "node_modules/vite": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.11.tgz", - "integrity": "sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==", + "version": "7.1.10", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.10.tgz", + "integrity": "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "esbuild": "^0.24.2", - "postcss": "^8.4.49", - "rollup": "^4.23.0" + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -13489,14 +14370,14 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", - "less": "*", + "less": "^4.0.0", "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" @@ -13538,17 +14419,17 @@ } }, "node_modules/vite-node": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.5.tgz", - "integrity": "sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", - "debug": "^4.4.0", - "es-module-lexer": "^1.6.0", - "pathe": "^2.0.2", - "vite": "^5.0.0 || ^6.0.0" + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" @@ -13560,32 +14441,67 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/vitest": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.5.tgz", - "integrity": "sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q==", + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", - "dependencies": { - "@vitest/expect": "3.0.5", - "@vitest/mocker": "3.0.5", - "@vitest/pretty-format": "^3.0.5", - "@vitest/runner": "3.0.5", - "@vitest/snapshot": "3.0.5", - "@vitest/spy": "3.0.5", - "@vitest/utils": "3.0.5", - "chai": "^5.1.2", - "debug": "^4.4.0", - "expect-type": "^1.1.0", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", "magic-string": "^0.30.17", - "pathe": "^2.0.2", - "std-env": "^3.8.0", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", - "tinypool": "^1.0.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.0.5", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "bin": { @@ -13601,8 +14517,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.0.5", - "@vitest/ui": "3.0.5", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, @@ -13630,6 +14546,19 @@ } } }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/vt-pbf": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", @@ -13703,13 +14632,13 @@ } }, "node_modules/whatwg-url": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", - "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", "dev": true, "license": "MIT", "dependencies": { - "tr46": "^5.0.0", + "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" }, "engines": { @@ -13718,6 +14647,8 @@ }, "node_modules/which": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -13731,6 +14662,8 @@ }, "node_modules/which-boxed-primitive": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, "license": "MIT", "dependencies": { @@ -13749,6 +14682,8 @@ }, "node_modules/which-builtin-type": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, "license": "MIT", "dependencies": { @@ -13775,6 +14710,8 @@ }, "node_modules/which-collection": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, "license": "MIT", "dependencies": { @@ -13791,14 +14728,17 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.18", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "for-each": "^0.3.3", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, @@ -13828,6 +14768,8 @@ }, "node_modules/word-wrap": { "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", "engines": { @@ -13845,6 +14787,8 @@ }, "node_modules/wrap-ansi": { "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", @@ -13861,6 +14805,8 @@ "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -13874,19 +14820,16 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -13899,6 +14842,8 @@ }, "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -13908,7 +14853,9 @@ } }, "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", "engines": { "node": ">=12" @@ -13924,9 +14871,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "dev": true, "license": "MIT", "engines": { @@ -13978,16 +14925,6 @@ "dev": true, "license": "ISC" }, - "node_modules/yaml": { - "version": "2.7.0", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/yaml-ast-parser": { "version": "0.0.43", "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", @@ -14007,6 +14944,8 @@ }, "node_modules/yocto-queue": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", "engines": { @@ -14017,28 +14956,19 @@ } }, "node_modules/zod": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", - "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", + "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", "license": "MIT", "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/zod-to-json-schema": { - "version": "3.24.5", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", - "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.24.1" - } - }, "node_modules/zustand": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.3.tgz", - "integrity": "sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz", + "integrity": "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==", "license": "MIT", "engines": { "node": ">=12.20.0" @@ -14066,6 +14996,8 @@ }, "node_modules/zwitch": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", "license": "MIT", "funding": { "type": "github", diff --git a/frontend/package.json b/frontend/package.json index dcc64a3ca..f5072a169 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,10 +13,12 @@ "type-check": "tsc --noEmit" }, "dependencies": { - "@ai-sdk/react": "^1.2.12", + "@ai-sdk/openai": "^2.0.52", + "@ai-sdk/react": "^2.0.72", "@bprogress/next": "^3.0.4", "@hookform/resolvers": "^3.10.0", "@nanostores/react": "github:ai/react", + "@next/env": "^15.5.5", "@radix-ui/react-checkbox": "^1.1.3", "@radix-ui/react-collapsible": "^1.1.2", "@radix-ui/react-dialog": "^1.1.6", @@ -26,10 +28,10 @@ "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-tooltip": "^1.1.8", - "@t3-oss/env-nextjs": "^0.12.0", "@tailwindcss/line-clamp": "^0.4.4", "@tailwindcss/typography": "^0.5.16", "@tanstack/react-query": "^5.66.0", + "ai": "^5.0.72", "buffer": "^6.0.3", "chart.js": "^4.4.7", "class-variance-authority": "^0.7.1", @@ -55,7 +57,7 @@ "sonner": "^2.0.1", "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7", - "zod": "^3.24.1", + "zod": "^4.1.12", "zustand": "^5.0.3" }, "devDependencies": { diff --git a/frontend/src/__tests__/lib/utils.test.ts b/frontend/src/__tests__/lib/utils.test.ts index 6f50f758f..9eb3d6d85 100644 --- a/frontend/src/__tests__/lib/utils.test.ts +++ b/frontend/src/__tests__/lib/utils.test.ts @@ -1,18 +1,29 @@ -import { describe, expect, test, beforeEach } from "vitest"; +// tests/utils.test.ts +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { describe, test, expect } from "vitest"; import { cn, convert_tools_to_set, + isToolPart, isLastMessageComplete, + getLastText, getToolInvocations, + getLastMessageText, getStorageID, getValidationStatus, - getStoppedStatus, -} from "@/lib/utils"; -import type { MessageStrict, Annotation } from "@/lib/types"; -import type { ToolInvocationUIPart } from "@ai-sdk/ui-utils"; + lastAssistantHasAllToolOutputs, +} from "@/lib/utils"; // adjust path if your project uses a different import alias + +// --- Helper types for tests (loose shapes to match runtime checks) --- +type Part = { type: string; text?: string; state?: string; output?: any }; +type MessageStrictShape = { + role?: string; + parts?: Part[]; + metadata?: { toolCalls?: any[] }; +}; describe("cn", () => { - test("merges class names with twMerge (e.g., overrides conflicting classes)", () => { + test("merges class names with twMerge (overrides conflicting classes)", () => { // If clsx produces "text-sm text-lg", twMerge should reduce to "text-lg" const result = cn("text-sm", "text-lg"); expect(result).toBe("text-lg"); @@ -20,200 +31,291 @@ describe("cn", () => { test("handles multiple inputs and falsy values correctly", () => { const result = cn("p-2", false && "block", "m-4"); + // falsy value should be ignored by clsx/twMerge expect(result).toBe("p-2 m-4"); }); }); describe("convert_tools_to_set", () => { - test("returns an object with each slug set to true and adds allchecked: true", () => { - const availableTools = [ - { slug: "alpha", label: "Alpha Tool" }, - { slug: "beta", label: "Beta Tool" }, + test("creates initial checked map with all tool slugs true and allchecked true", () => { + const tools = [ + { slug: "tool-a", label: "Tool A" }, + { slug: "tool-b", label: "Tool B" }, ]; - const result = convert_tools_to_set(availableTools); - expect(result).toEqual({ - alpha: true, - beta: true, + const set = convert_tools_to_set(tools); + expect(set).toEqual({ + "tool-a": true, + "tool-b": true, allchecked: true, }); }); - test("works for an empty array of availableTools", () => { - const result = convert_tools_to_set([]); - expect(result).toEqual({ allchecked: true }); + test("works for empty input", () => { + const set = convert_tools_to_set([]); + expect(set).toEqual({ allchecked: true }); }); }); -describe("isLastMessageComplete", () => { - test("returns true when `messages` is undefined", () => { - expect(isLastMessageComplete(undefined)).toBe(true); +describe("isToolPart", () => { + test("returns true for parts whose type starts with 'tool-'", () => { + const toolPart: Part = { type: "tool-run", output: "ok" }; + expect(isToolPart(toolPart as any)).toBe(true); }); - test("returns true when annotations array is empty", () => { - const msg = { annotations: [] } as unknown as MessageStrict; - expect(isLastMessageComplete(msg)).toBe(true); + test("returns false for non-tool parts", () => { + const textPart: Part = { type: "text", text: "hello" }; + expect(isToolPart(textPart as any)).toBe(false); }); +}); - test("returns false if any annotation has isComplete === false", () => { - const msg = { - annotations: [{ isComplete: true }, { isComplete: false }], - } as MessageStrict; - expect(isLastMessageComplete(msg)).toBe(false); +describe("isLastMessageComplete", () => { + test("returns true if metadata.toolCalls is empty or undefined", () => { + const msg1: MessageStrictShape = { metadata: { toolCalls: [] } }; + const msg2: MessageStrictShape = {}; + expect(isLastMessageComplete(msg1 as any)).toBe(true); + expect(isLastMessageComplete(msg2 as any)).toBe(true); }); - test("returns true if all annotations are complete", () => { - const msg = { - annotations: [{ isComplete: true }, { isComplete: true }], - } as MessageStrict; - expect(isLastMessageComplete(msg)).toBe(true); + test("returns false if any tool call has isComplete === false", () => { + const msg: MessageStrictShape = { + metadata: { + toolCalls: [ + { toolCallId: "1", isComplete: true }, + { toolCallId: "2", isComplete: false }, + ], + }, + }; + expect(isLastMessageComplete(msg as any)).toBe(false); }); -}); - -describe("getToolInvocations", () => { - test("extracts toolInvocation objects from parts where type === 'tool-invocation'", () => { - // We force-cast to ToolInvocationUIPart via unknown to satisfy the compiler - const fakeInvocation = { id: "inv-1", name: "fake-tool" }; - const part = { - type: "tool-invocation", - toolInvocation: fakeInvocation, - } as unknown as ToolInvocationUIPart; - // Similarly, cast the entire message to MessageStrict via unknown - const message = { - parts: [part, { type: "text", text: "just text" }], - } as unknown as MessageStrict; - - const invocations = getToolInvocations(message); - expect(invocations).toEqual([fakeInvocation]); + test("returns true if all tool calls are complete (or missing isComplete)", () => { + const msg: MessageStrictShape = { + metadata: { + toolCalls: [{ toolCallId: "1", isComplete: true }, { toolCallId: "2" }], + }, + }; + // The function treats missing isComplete as not explicitly false, so returns true + expect(isLastMessageComplete(msg as any)).toBe(true); }); +}); - test("returns an empty array when message or parts is undefined", () => { - expect(getToolInvocations(undefined)).toEqual([]); - - // Cast through unknown so that MessageStrict with undefined parts compiles - const msgNoParts = { parts: undefined } as unknown as MessageStrict; - expect(getToolInvocations(msgNoParts)).toEqual([]); +describe("getLastText / getLastMessageText", () => { + test("getLastText returns the last text part of a MessageStrict", () => { + const message: MessageStrictShape = { + parts: [ + { type: "text", text: "first" }, + { type: "something", text: "ignored" }, + { type: "text", text: "last" }, + ], + }; + expect(getLastText(message as any)).toBe("last"); }); -}); -describe("getStorageID", () => { - test("extracts a single storage_id from a JSON string", () => { - const toolCall = { - type: "tool-invocation", - toolInvocation: { - state: "result", - result: JSON.stringify({ storage_id: "abc-123" }), - }, - } as ToolInvocationUIPart; - const ids = getStorageID(toolCall); - expect(ids).toEqual(["abc-123"]); + test("getLastText returns empty string if no text parts", () => { + const message: MessageStrictShape = { + parts: [{ type: "tool-run", output: "ok" }], + }; + expect(getLastText(message as any)).toBe(""); }); - test("extracts multiple storage_ids from an object", () => { - const toolCall = { - type: "tool-invocation", - toolInvocation: { - state: "result", - result: { storage_id: ["id1", "id2"] }, + test("getLastMessageText returns last message's last text", () => { + const messages = [ + { role: "assistant", parts: [{ type: "text", text: "one" }] }, + { + role: "assistant", + parts: [ + { type: "text", text: "two" }, + { type: "text", text: "final" }, + ], }, - } as ToolInvocationUIPart; - - const ids = getStorageID(toolCall); - expect(ids).toEqual(["id1", "id2"]); + ]; + expect(getLastMessageText(messages as any)).toBe("final"); }); - test("returns an empty array if result is invalid JSON string", () => { - const toolCall = { - type: "tool-invocation", - toolInvocation: { - state: "result", - result: "not json", - }, - } as ToolInvocationUIPart; + test("getLastMessageText returns empty string for empty messages", () => { + expect(getLastMessageText([])).toBe(""); + }); +}); - const ids = getStorageID(toolCall); - expect(ids).toEqual([]); +describe("getToolInvocations", () => { + test("extracts tool parts from a MessageStrict", () => { + const msg: MessageStrictShape = { + parts: [ + { type: "text", text: "hi" }, + { type: "tool-run", output: "ok" }, + { type: "tool-download", output: { id: 1 } }, + ], + }; + const invocations = getToolInvocations(msg as any); + expect(invocations.length).toBe(2); + expect(invocations.map((p) => p.type)).toEqual([ + "tool-run", + "tool-download", + ]); }); - test("returns an empty array if storage_id is missing", () => { - const toolCall = { - type: "tool-invocation", - toolInvocation: { - state: "result", - result: { some_other_field: "value" }, - }, - } as ToolInvocationUIPart; + test("returns empty array for undefined or no tool parts", () => { + expect(getToolInvocations(undefined)).toEqual([]); + expect( + getToolInvocations({ parts: [{ type: "text", text: "x" }] } as any), + ).toEqual([]); + }); +}); - const ids = getStorageID(toolCall); - expect(ids).toEqual([]); +describe("getStorageID", () => { + test("returns storage ids when output is an object with storage_id array", () => { + const toolCall: Part = { + type: "tool-upload", + state: "output-available", + output: { storage_id: ["id1", "id2"] }, + }; + const ids = getStorageID(toolCall as any); + expect(ids).toEqual(["id1", "id2"]); }); - test("returns empty array if toolCall is undefined", () => { - const ids = getStorageID(undefined); - expect(ids).toEqual([]); + test("returns single storage id when output.storage_id is a string", () => { + const toolCall: Part = { + type: "tool-upload", + state: "output-available", + output: { storage_id: "single-id" }, + }; + const ids = getStorageID(toolCall as any); + expect(ids).toEqual(["single-id"]); }); - test("returns empty array if type is not 'tool-invocation'", () => { - const toolCall = { - type: "not-a-tool-invocation", - toolInvocation: { - state: "result", - result: { storage_id: "abc-123" }, - }, - } as unknown as ToolInvocationUIPart; + test("parses a JSON-string output and extracts storage_id", () => { + const toolCall: Part = { + type: "tool-upload", + state: "output-available", + output: JSON.stringify({ storage_id: "str-id" }), + }; + const ids = getStorageID(toolCall as any); + expect(ids).toEqual(["str-id"]); + }); - const ids = getStorageID(toolCall); - expect(ids).toEqual([]); + test("returns empty array when not output-available or missing storage_id", () => { + const wrongState: Part = { + type: "tool-upload", + state: "running", + output: { storage_id: "x" }, + }; + expect(getStorageID(wrongState as any)).toEqual([]); + + const noStorageField: Part = { + type: "tool-upload", + state: "output-available", + output: { foo: "bar" }, + }; + expect(getStorageID(noStorageField as any)).toEqual([]); + + const invalidJsonOutput: Part = { + type: "tool-upload", + state: "output-available", + output: "{ not valid json", + }; + // safeParse will return original string which doesn't have storage_id, so result is [] + expect(getStorageID(invalidJsonOutput as any)).toEqual([]); }); }); describe("getValidationStatus", () => { - let annotations: Annotation[]; - - beforeEach(() => { - annotations = [ - { toolCallId: "toolA", validated: "accepted" } as Annotation, - { toolCallId: "toolB", validated: "rejected" } as Annotation, - ]; + test("returns validated status for a matching toolCallId", () => { + const metadata = { + toolCalls: [ + { toolCallId: "a", validated: true }, + { toolCallId: "b", validated: false }, + ], + }; + expect(getValidationStatus(metadata as any, "a")).toBe(true); + expect(getValidationStatus(metadata as any, "b")).toBe(false); }); - test("returns the correct validated value when annotation is found", () => { - expect(getValidationStatus(annotations, "toolA")).toBe("accepted"); - expect(getValidationStatus(annotations, "toolB")).toBe("rejected"); + test("returns undefined when not found", () => { + const metadata = { toolCalls: [{ toolCallId: "x", validated: true }] }; + expect(getValidationStatus(metadata as any, "nope")).toBeUndefined(); + expect(getValidationStatus(undefined as any, "nope")).toBeUndefined(); }); +}); - test("returns undefined when annotation with given toolCallId does not exist", () => { - expect(getValidationStatus(annotations, "nonexistent")).toBeUndefined(); +describe("lastAssistantHasAllToolOutputs", () => { + test("returns false for invalid messages shape", () => { + // Not an array + // @ts-expect-error : since it is not an array, needed. + expect(lastAssistantHasAllToolOutputs({ messages: null })).toBe(false); }); - test("returns undefined when annotations array is undefined", () => { - expect(getValidationStatus(undefined, "toolA")).toBeUndefined(); + test("returns false if last message is not assistant", () => { + const useChatReturn = { + messages: [ + { + role: "user", + parts: [{ type: "tool-a", state: "output-available", output: {} }], + }, + ], + }; + expect(lastAssistantHasAllToolOutputs(useChatReturn as any)).toBe(false); }); -}); -describe("getStoppedStatus", () => { - let annotations: Annotation[]; - - beforeEach(() => { - annotations = [ - { toolCallId: "tool1", isComplete: false } as Annotation, - { toolCallId: "tool2", isComplete: true } as Annotation, - ]; + test("returns false if last assistant message has trailing text", () => { + const useChatReturn = { + messages: [ + { + role: "assistant", + parts: [{ type: "text", text: "I am thinking..." }], + }, + ], + }; + expect(lastAssistantHasAllToolOutputs(useChatReturn as any)).toBe(false); }); - test("returns true when annotation.isComplete is false", () => { - expect(getStoppedStatus(annotations, "tool1")).toBe(true); + test("returns false if there are no tool parts", () => { + const useChatReturn = { + messages: [ + { role: "assistant", parts: [{ type: "something", text: "" }] }, + ], + }; + expect(lastAssistantHasAllToolOutputs(useChatReturn as any)).toBe(false); }); - test("returns false when annotation.isComplete is true", () => { - expect(getStoppedStatus(annotations, "tool2")).toBe(false); + test("returns true when last assistant message has only tool parts and all have outputs or output-available state", () => { + const useChatReturn = { + messages: [ + { + role: "assistant", + parts: [ + { type: "tool-a", state: "output-available", output: { foo: 1 } }, + { type: "tool-b", state: "output-available", output: "ok" }, + ], + }, + ], + }; + expect(lastAssistantHasAllToolOutputs(useChatReturn as any)).toBe(true); }); - test("returns false when annotation is missing (undefined)", () => { - expect(getStoppedStatus(annotations, "missingTool")).toBe(false); + test("returns true if parts have no 'state' but have truthy output", () => { + const useChatReturn = { + messages: [ + { + role: "assistant", + parts: [{ type: "tool-x", output: { something: 1 } }], + }, + ], + }; + expect(lastAssistantHasAllToolOutputs(useChatReturn as any)).toBe(true); }); - test("returns false when annotations array is undefined", () => { - expect(getStoppedStatus(undefined, "tool1")).toBe(false); + test("returns false if any tool part lacks output and is not output-available", () => { + const useChatReturn = { + messages: [ + { + role: "assistant", + parts: [ + { type: "tool-a", state: "output-available", output: { ok: true } }, + { type: "tool-b", state: "running" }, // missing output + ], + }, + ], + }; + expect(lastAssistantHasAllToolOutputs(useChatReturn as any)).toBe(false); }); }); diff --git a/frontend/src/components/chat/chat-input-inside-thread.tsx b/frontend/src/components/chat/chat-input-inside-thread.tsx index 0bd2d4aef..218d7b28c 100644 --- a/frontend/src/components/chat/chat-input-inside-thread.tsx +++ b/frontend/src/components/chat/chat-input-inside-thread.tsx @@ -8,6 +8,7 @@ import { useQueryClient } from "@tanstack/react-query"; import { resetInfiniteQueryPagination } from "@/hooks/get-message-page"; import { ModelSelectionDropdown } from "./model-selection"; import { LLMModel } from "@/lib/types"; +import { MessageStrict } from "@/lib/types"; type ChatInputInsideThreadProps = { input: string; @@ -19,14 +20,21 @@ type ChatInputInsideThreadProps = { threadId: string; setCheckedTools: (tools: Record) => void; setCurrentModel: (model: LLMModel) => void; - handleInputChange: (e: React.ChangeEvent) => void; - handleSubmit: (event?: { preventDefault?: () => void }) => void; + handleInputChange: Dispatch>; + handleSubmit: ( + e: React.FormEvent, + ) => void; setIsAutoScrollEnabled: (enabled: boolean) => void; hasOngoingToolInvocations: boolean; onStop: () => void; stopped: boolean; setStopped: Dispatch>; setIsInvalidating: Dispatch>; + setMessages: ( + messages: + | MessageStrict[] + | ((messages: MessageStrict[]) => MessageStrict[]), + ) => void; }; export function ChatInputInsideThread({ @@ -46,6 +54,7 @@ export function ChatInputInsideThread({ onStop, stopped, setStopped, + setMessages, setIsInvalidating, }: ChatInputInsideThreadProps) { const canSend = !hasOngoingToolInvocations || stopped; @@ -89,7 +98,7 @@ export function ChatInputInsideThread({ name="prompt" placeholder="Message the AI..." value={input} - onChange={handleInputChange} + onChange={(e) => handleInputChange(e.target.value)} onKeyDown={(e) => handleKeyDown(e)} autoComplete="off" maxRows={10} @@ -116,6 +125,14 @@ export function ChatInputInsideThread({ e.preventDefault(); onStop(); setStopped(true); + setMessages((prevState) => { + prevState[prevState.length - 1] = { + ...prevState[prevState.length - 1], + isComplete: false, + }; + // We only change the metadata at message level and keep the rest. + return prevState; + }); startTransition(() => { resetInfiniteQueryPagination( queryClient, diff --git a/frontend/src/components/chat/chat-message-tool.tsx b/frontend/src/components/chat/chat-message-tool.tsx index 663694b5d..a3156b456 100644 --- a/frontend/src/components/chat/chat-message-tool.tsx +++ b/frontend/src/components/chat/chat-message-tool.tsx @@ -3,7 +3,7 @@ import { useState, useEffect } from "react"; import { MessageStrict } from "@/lib/types"; import { HumanValidationDialog } from "@/components/chat/human-validation-dialog"; -import { ToolInvocation } from "@ai-sdk/ui-utils"; +import { ToolUIPart } from "ai"; import { useExecuteTool } from "@/hooks/tools"; import { ToolCallCollapsible } from "@/components/chat/tool-call-collapsible"; import React from "react"; @@ -11,17 +11,31 @@ import React from "react"; type ChatMessageToolProps = { content?: string; threadId: string; - tool: ToolInvocation; + tool: ToolUIPart; stopped: boolean; availableTools: Array<{ slug: string; label: string }>; - addToolResult: ({ + addToolResult: ({ + state, + tool, toolCallId, - result, - }: { - toolCallId: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - result: any; - }) => void; + output, + errorText, + }: + | { + state?: "output-available"; + tool: TOOL; + toolCallId: string; + output: unknown; + errorText?: never; + } + | { + state: "output-error"; + tool: TOOL; + toolCallId: string; + output?: never; + errorText: string; + }) => Promise; + validated: "pending" | "accepted" | "rejected" | "not_required"; setMessage: (updater: (msg: MessageStrict) => MessageStrict) => void; }; @@ -52,20 +66,27 @@ export const ChatMessageTool = function ChatMessageTool({ setValidationError(null); // We leverage the addToolResult from useChat to add results. // It will also trigger the chat automatically when every tool has results ! - addToolResult({ toolCallId: tool.toolCallId, result: data.content }); + addToolResult({ + state: "output-available", + tool: tool.type, + toolCallId: tool.toolCallId, + output: data.content, + }); - // If the tool had a validation error, we have to reset the annotation. + // If the tool had a validation error, we have to reset the metadata. } else if (data.status === "validation-error") { setValidationError(data.content || "Validation failed"); setMessage((msg) => { return { ...msg, - annotations: [ - ...(msg.annotations || []).filter( - (a) => a.toolCallId !== tool.toolCallId, - ), - { toolCallId: tool.toolCallId, validated: "pending" }, - ], + metadata: { + toolCalls: [ + ...(msg.metadata?.toolCalls || []).filter( + (a) => a.toolCallId !== tool.toolCallId, + ), + { toolCallId: tool.toolCallId, validated: "pending" }, + ], + }, }; }); } @@ -74,18 +95,18 @@ export const ChatMessageTool = function ChatMessageTool({ }, [status]); const toolLabel = - availableTools.filter((toolObj) => toolObj.slug === tool.toolName)?.[0] - ?.label ?? tool.toolName; + availableTools.filter((toolObj) => toolObj.slug === tool.type.slice(5))?.[0] + ?.label ?? tool.type; return (
; - addToolResult: ({ + addToolResult: ({ + state, + tool, toolCallId, - result, - }: { - toolCallId: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - result: any; - }) => void; + output, + errorText, + }: + | { + state?: "output-available"; + tool: TOOL; + toolCallId: string; + output: unknown; + errorText?: never; + } + | { + state: "output-error"; + tool: TOOL; + toolCallId: string; + output?: never; + errorText: string; + }) => Promise; setMessages: ( messages: | MessageStrict[] @@ -59,7 +73,7 @@ export function ChatMessagesInsideThread({ key={`${message.id}-reasoning`} reasoningSteps={message.parts ?.filter((part) => part.type === "reasoning") - .map((part) => part.reasoning)} + .map((part) => part.text)} messageId={message.id} isReasoning={ !(loadingStatus === "ready") && idx === messages.length - 1 @@ -67,24 +81,20 @@ export function ChatMessagesInsideThread({ /> )} {message.parts?.map((part, partId) => { - if (part.type === "tool-invocation") { + if (isToolPart(part)) { const validated = - getValidationStatus( - message.annotations, - part.toolInvocation.toolCallId, - ) ?? "not_required"; - const stopped = getStoppedStatus( - message.annotations, - part.toolInvocation.toolCallId, - ); + getValidationStatus(message.metadata, part.toolCallId) ?? + "not_required"; + const isStopped = + message.metadata?.toolCalls?.some( + (e) => e.toolCallId == part.toolCallId && !e.isComplete, + ) || false; return ( -
+
) : ( - + ), )} {loadingStatus !== "ready" && } diff --git a/frontend/src/components/chat/chat-page.tsx b/frontend/src/components/chat/chat-page.tsx index 2fc20f1f9..515f8b5a6 100644 --- a/frontend/src/components/chat/chat-page.tsx +++ b/frontend/src/components/chat/chat-page.tsx @@ -13,8 +13,14 @@ import { ChatMessagesInsideThread } from "@/components/chat/chat-messages-inside import { generateEditTitle } from "@/actions/generate-edit-thread"; import { toast } from "sonner"; import { useGetMessageNextPage } from "@/hooks/get-message-page"; -import { getToolInvocations, isLastMessageComplete } from "@/lib/utils"; -import { md5 } from "js-md5"; +import { + getLastMessageText, + getLastText, + getToolInvocations, + isLastMessageComplete, + lastAssistantHasAllToolOutputs, +} from "@/lib/utils"; +import { DefaultChatTransport } from "ai"; type ChatPageProps = { threadId: string; @@ -42,8 +48,6 @@ export function ChatPage({ const setNewMessage = useStore((state) => state.setNewMessage); const hasSendFirstMessage = useRef(false); // Scrolling and pagination - const prevHeight = useRef(0); - const prevScroll = useRef(0); const observerRef = useRef(null); const topSentinelRef = useRef(null); const messagesEndRef = useRef(null); @@ -75,35 +79,48 @@ export function ChatPage({ const { addToolResult, - append, error, messages: messagesRaw, - handleInputChange, - handleSubmit, - input, setMessages: setMessagesRaw, + sendMessage, status, stop, } = useChat({ - api: `${env.NEXT_PUBLIC_BACKEND_URL}/qa/chat_streamed/${threadId}`, - headers: { - Authorization: `Bearer ${session?.accessToken}`, - }, - initialMessages: retrievedMessages, - experimental_prepareRequestBody: ({ messages }) => { - const lastMessage = messages[messages.length - 1]; - const selectedTools = Object.keys(checkedTools).filter( - (key) => key !== "allchecked" && checkedTools[key] === true, - ); - return { - content: lastMessage.content, - tool_selection: selectedTools, - model: currentModel.id, - frontend_url: frontendUrl, - }; - }, + messages: retrievedMessages, + experimental_throttle: 50, + sendAutomaticallyWhen: lastAssistantHasAllToolOutputs, + transport: new DefaultChatTransport({ + api: `${env.NEXT_PUBLIC_BACKEND_URL}/qa/chat_streamed/${threadId}`, + headers: { + Authorization: `Bearer ${session?.accessToken}`, + }, + prepareSendMessagesRequest: ({ messages }) => { + const checkedToolsNow = useStore.getState().checkedTools; + const currentModelNow = useStore.getState().currentModel; + return { + body: { + content: getLastMessageText(messages), + tool_selection: Object.keys(checkedToolsNow).filter( + (key) => key !== "allchecked" && checkedToolsNow[key] === true, + ), + model: currentModelNow.id, + frontend_url: frontendUrl, + }, + }; + }, + }), }); + // Handle chat inputs. + const [input, setInput] = useState(""); + const handleSubmit = ( + e: React.FormEvent, + ) => { + e.preventDefault(); + sendMessage({ text: input }); + setInput(""); + }; + // This should probably be changed to be more granular, I just created the old behaviour here. const isLoading = status == "streaming" || status == "submitted"; @@ -124,11 +141,7 @@ export function ChatPage({ !hasSendFirstMessage.current ) { hasSendFirstMessage.current = true; - append({ - id: "temp_id", - role: "user", - content: newMessage, - }); + sendMessage({ text: newMessage }); generateEditTitle(null, threadId, newMessage); setNewMessage(""); } @@ -151,24 +164,6 @@ export function ChatPage({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - // Handle streaming interruption - useEffect(() => { - if (stopped) { - setMessages((prevState) => { - prevState[prevState.length - 1] = { - ...prevState[prevState.length - 1], - annotations: prevState - .at(-1) - ?.annotations?.map((ann) => - !ann.toolCallId ? { isComplete: false } : ann, - ), - }; - // We only change the annotation at message level and keep the rest. - return prevState; - }); - } - }, [stopped, setMessages]); - useEffect(() => { if (isInvalidating || isFetching) return; // Set retrieved DB messaged as current messages @@ -176,18 +171,19 @@ export function ChatPage({ setMessages(() => [ ...retrievedMessages, ...messages.filter( - (m) => m.id.length !== 36 && !m.id.startsWith("temp"), + (m) => m.id.length !== 36 && !m.id.startsWith("msg"), ), ]); } else { setMessages(retrievedMessages); } - }, [md5(JSON.stringify(retrievedMessages))]); // Rerun on content change + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isInvalidating, isFetching]); // RE-run on new fetching or stop // Constant to check if there are tool calls at the end of conv. const hasOngoingToolInvocations = (getToolInvocations(messages.at(-1)) ?? []).length > 0 && - messages.at(-1)?.content == ""; + getLastText(messages.at(-1)) == ""; // Auto scroll when streaming useEffect(() => { @@ -212,38 +208,53 @@ export function ChatPage({ // Observer to fetch new pages : useEffect(() => { + const container = containerRef.current; + const sentinel = topSentinelRef.current; + + if (!container || !sentinel) return; + + if (observerRef.current) { + observerRef.current.disconnect(); + } + observerRef.current = new IntersectionObserver( async (entries) => { + const entry = entries[0]; + if ( - entries[0].isIntersecting && + entry.isIntersecting && + hasNextPage && !isFetchingPreviousPage && !isLoading ) { - const el = containerRef.current!; - prevHeight.current = el.scrollHeight; - prevScroll.current = el.scrollTop; - if (!hasNextPage) return; - await fetchPreviousPage(); - if (!isFetchingPreviousPage && !isLoading && prevHeight.current) { + const scrollFromBottom = container.scrollHeight - container.scrollTop; + + try { + await fetchPreviousPage(); + requestAnimationFrame(() => { - const heightDiff = el.scrollHeight - prevHeight.current; - el.scrollTop = prevScroll.current + heightDiff - 40; + container.scrollTop = + container.scrollHeight - scrollFromBottom - 40; }); + } catch (error) { + console.error("Error fetching previous page:", error); } } }, { - root: containerRef.current, + root: container, + rootMargin: "0px 0px 200px 0px", + threshold: 0.1, }, ); - const sentinel = topSentinelRef.current; - if (sentinel && observerRef.current) observerRef.current.observe(sentinel); - // Remove intersection listener when unmounted + observerRef.current.observe(sentinel); + return () => { - if (sentinel && observerRef.current) - observerRef.current.unobserve(sentinel); - if (observerRef.current) observerRef.current.disconnect(); + if (observerRef.current) { + observerRef.current.disconnect(); + observerRef.current = null; + } }; }, [hasNextPage, isFetchingPreviousPage, isLoading, fetchPreviousPage]); @@ -314,13 +325,14 @@ export function ChatPage({ threadId={threadId} setCheckedTools={setCheckedTools} setCurrentModel={setCurrentModel} - handleInputChange={handleInputChange} + handleInputChange={setInput} handleSubmit={handleSubmit} hasOngoingToolInvocations={hasOngoingToolInvocations} setIsAutoScrollEnabled={setIsAutoScrollEnabled} onStop={stop} stopped={stopped} setStopped={setStopped} + setMessages={setMessages} setIsInvalidating={setIsInvalidating} />
diff --git a/frontend/src/components/chat/human-validation-dialog.tsx b/frontend/src/components/chat/human-validation-dialog.tsx index 8f795e9b8..3fa6dce50 100644 --- a/frontend/src/components/chat/human-validation-dialog.tsx +++ b/frontend/src/components/chat/human-validation-dialog.tsx @@ -1,7 +1,6 @@ "use client"; -import { useState, useEffect, useRef } from "react"; -import { useTheme } from "next-themes"; +import { useState, useRef } from "react"; import { Dialog, DialogContent, @@ -10,23 +9,15 @@ import { DialogTitle, DialogFooter, } from "@/components/ui/dialog"; -import { - JsonData, - JsonEditor, - monoDarkTheme, - monoLightTheme, -} from "json-edit-react"; -import { scsPostSchema } from "@/lib/zod-schemas"; -import { toast } from "sonner"; +import { JsonData } from "json-edit-react"; import { Button } from "@/components/ui/button"; import type { MessageStrict } from "@/lib/types"; import { HilRefusalFeedbackDialog } from "@/components/chat/hil-refusal-feedback-dialog"; -import { getToolInvocations } from "@/lib/utils"; type HumanValidationDialogProps = { threadId: string; - toolId: string; + toolCallId: string; toolName: string; availableTools: Array<{ slug: string; label: string }>; args: JsonData; @@ -37,14 +28,13 @@ type HumanValidationDialogProps = { threadId: string; toolCallId: string; validation: "accepted" | "rejected"; - args?: string; feedback?: string; }) => void; }; export function HumanValidationDialog({ threadId, - toolId, + toolCallId, toolName, availableTools, args, @@ -53,41 +43,22 @@ export function HumanValidationDialog({ setMessage, mutate, }: HumanValidationDialogProps) { - const { theme } = useTheme(); - const isLightTheme = theme === "light"; - - const [editedArgs, setEditedArgs] = useState(args); - const [isEdited, setIsEdited] = useState(false); const [isAccepted, setIsAccepted] = useState<"accepted" | "rejected">( "rejected", ); - const [error, setError] = useState(""); const [showReviewDialog, setShowReviewDialog] = useState(true); const [showFeedbackDialog, setShowFeedbackDialog] = useState(false); const [dialogTransition, setDialogTransition] = useState(false); const [feedback, setFeedback] = useState(""); const formRef = useRef(null); - useEffect(() => { - setEditedArgs(args); - }, [args]); - - const handleArgsChange = (value: JsonData) => { - setEditedArgs(value); - setIsEdited(JSON.stringify(value) !== JSON.stringify(args)); - }; - const handleOpenChange = (open: boolean) => { if (!open) { - // Reset the state when the dialog is closed setTimeout(() => { - setEditedArgs(args); - setIsEdited(false); setShowReviewDialog(true); setShowFeedbackDialog(false); setFeedback(""); setDialogTransition(false); - setError(""); }, 300); } setIsOpen(open); @@ -104,47 +75,44 @@ export function HumanValidationDialog({ const handleAction = (formData: FormData) => { const validation = formData.get("validation") as "accepted" | "rejected"; - setError(""); - // Process the decision first - try { - setMessage((msg: MessageStrict) => { - const updatedMsg = { - ...msg, - annotations: [ - ...(msg.annotations || []).filter((a) => a.toolCallId !== toolId), - { - toolCallId: toolId, - validated: validation, - }, - ], - toolInvocations: [ - ...(getToolInvocations(msg) || []).filter( - (t) => t.toolCallId !== toolId, + + setMessage((msg: MessageStrict) => { + const updatedParts = msg.parts.map((part) => { + if ( + part.type.startsWith("tool-") && + "toolCallId" in part && + part.toolCallId === toolCallId + ) { + return { + ...part, + input: args, + state: "input-available" as const, + output: undefined, + errorText: undefined, + } as typeof part; + } + return part; + }); + + return { + ...msg, + parts: updatedParts as typeof msg.parts, + metadata: { + ...msg.metadata, + toolCalls: [ + ...(msg.metadata?.toolCalls || []).filter( + (a) => a.toolCallId !== toolCallId, ), - { - toolCallId: toolId, - toolName: toolName, - args: isEdited ? editedArgs : args, - state: "call" as const, - }, + { toolCallId: toolCallId, validated: validation, isComplete: true }, ], - }; - return updatedMsg; - }); - } catch { - // Timeout is here to have the flickering effect when clicking - // "Accept" multiple times on a malformed JSON. - setTimeout(() => { - setError("Invalid JSON. Please check your input and try again."); - }, 50); - return; - } - // Execute using the passed mutate function + }, + } as MessageStrict; + }); + mutate({ threadId, - toolCallId: toolId, + toolCallId: toolCallId, validation, - args: isEdited ? JSON.stringify(editedArgs) : JSON.stringify(args), feedback: feedback === "" ? undefined : feedback, }); @@ -155,15 +123,7 @@ export function HumanValidationDialog({
- - - -
{showReviewDialog && ( @@ -184,72 +144,9 @@ export function HumanValidationDialog({

Arguments:

- { - const result = scsPostSchema.safeParse(newData); - if (!result.success) { - const errorMessage = result.error.errors - .map( - (error) => - `${error.path.join(".")}${error.path.length ? ": " : ""}${error.message}`, - ) - .join("\n"); - toast.error( - <> - JSON Validation Error -
{errorMessage}
- , - ); - return "JSON Schema error"; - } - - handleArgsChange(result.data); - }} - setData={(data: JsonData) => handleArgsChange(data)} - className="max-h-[75vh] overflow-y-auto" - theme={[ - isLightTheme ? monoLightTheme : monoDarkTheme, - { - styles: { - container: { - backgroundColor: isLightTheme - ? "#f1f1f1" - : "#151515", - fontFamily: "Geist Mono", - }, - input: isLightTheme ? "#575757" : "#a8a8a8", - inputHighlight: isLightTheme - ? "#b3d8ff" - : "#1c3a59", - string: isLightTheme - ? "rgb(8, 129, 215)" - : "rgb(38, 139, 210)", - number: isLightTheme - ? "rgb(8, 129, 215)" - : "rgb(38, 139, 210)", - boolean: isLightTheme - ? "rgb(8, 129, 215)" - : "rgb(38, 139, 210)", - }, - }, - ]} - maxWidth={1000} - restrictTypeSelection={true} - rootName={"JSON"} - showStringQuotes={true} - showArrayIndices={false} - showCollectionCount={false} - restrictDelete={true} - /> - {error && ( -

- {error} -

- )} +
+                      {JSON.stringify(args, null, 2)}
+                    
diff --git a/frontend/src/components/chat/model-selection.tsx b/frontend/src/components/chat/model-selection.tsx index 153da8d42..407d9b614 100644 --- a/frontend/src/components/chat/model-selection.tsx +++ b/frontend/src/components/chat/model-selection.tsx @@ -131,7 +131,7 @@ export function ModelSelectionDropdown({ {availableModels.map((model) => { - const isCurrentModel = model.name === currentModel.name; + const isCurrentModel = model.id === currentModel.id; return renderModelItem(model, isCurrentModel); })} diff --git a/frontend/src/components/chat/tool-call-collapsible.tsx b/frontend/src/components/chat/tool-call-collapsible.tsx index f49c2b89f..f5f24e518 100644 --- a/frontend/src/components/chat/tool-call-collapsible.tsx +++ b/frontend/src/components/chat/tool-call-collapsible.tsx @@ -11,10 +11,10 @@ import { import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { ToolStatusBadge } from "@/components/chat/tool-call-status"; -import { ToolInvocation } from "@ai-sdk/ui-utils"; +import { ToolUIPart } from "ai"; type ToolCallCollapsibleProps = { - tool: ToolInvocation; + tool: ToolUIPart; toolLabel: string; stopped: boolean; validated: "pending" | "accepted" | "rejected" | "not_required"; @@ -122,7 +122,7 @@ export function ToolCallCollapsible({ asChild className="h-auto p-1 text-muted-foreground hover:text-blue-600 dark:hover:text-blue-400" > - + @@ -140,7 +140,7 @@ export function ToolCallCollapsible({
-                      {JSON.stringify(tool?.args, null, 2)}
+                      {JSON.stringify(tool?.input, null, 2)}
                     
@@ -161,7 +161,7 @@ export function ToolCallCollapsible({
)} - {tool?.state === "result" && ( + {tool?.state === "output-available" && (
@@ -172,26 +172,26 @@ export function ToolCallCollapsible({ ) : ( handleCopy(tool?.result)} + onClick={() => handleCopy(tool?.output as string)} /> )}
-                        {typeof tool?.result === "string"
+                        {typeof tool?.output === "string"
                           ? (() => {
                               try {
                                 return JSON.stringify(
-                                  JSON.parse(tool?.result),
+                                  JSON.parse(tool?.output),
                                   null,
                                   2,
                                 );
                               } catch {
-                                return tool?.result;
+                                return tool?.output;
                               }
                             })()
-                          : JSON.stringify(tool?.result, null, 2)}
+                          : JSON.stringify(tool?.output, null, 2)}
                       
diff --git a/frontend/src/components/chat/tool-call-status.tsx b/frontend/src/components/chat/tool-call-status.tsx index 1b4e3d055..2942613e3 100644 --- a/frontend/src/components/chat/tool-call-status.tsx +++ b/frontend/src/components/chat/tool-call-status.tsx @@ -1,7 +1,11 @@ import { ReactElement } from "react"; import { Check, X, Loader2, AlertCircle } from "lucide-react"; -export type ToolState = "call" | "result" | "partial-call"; +export type ToolState = + | "input-streaming" + | "input-available" + | "output-available" + | "output-error"; export type ValidationStatus = | "pending" | "accepted" @@ -23,11 +27,11 @@ export function ToolStatusBadge({ }: ToolStatusBadgeProps): ReactElement { const getStatusIcon = (): ReactElement => { if (stopped) return ; - if (state === "result") { + if (state === "output-available") { if (validated === "rejected") return ; return ; } - if (state === "call") { + if (state === "input-available") { if (validated === "pending") return ; if (validated === "accepted") @@ -41,12 +45,12 @@ export function ToolStatusBadge({ if (stopped) { return "text-red-700 bg-red-200 hover:bg-red-300 dark:text-red-200 dark:bg-red-800/90 dark:hover:bg-red-700/90"; } - if (state === "result") { + if (state === "output-available") { if (validated === "rejected") return "text-red-700 bg-red-200 hover:bg-red-300 dark:text-red-200 dark:bg-red-800/90 dark:hover:bg-red-700/90"; return "text-green-800 bg-green-200 hover:bg-green-300 dark:text-green-200 dark:bg-green-800/90 dark:hover:bg-green-700/90"; } - if (state === "call") { + if (state === "input-available") { if (validated === "pending") return "text-orange-700 hover:bg-orange-300 dark:text-orange-200 dark:hover:bg-orange-700/90"; if (validated === "accepted") @@ -61,11 +65,11 @@ export function ToolStatusBadge({ if (stopped) { return "Stopped"; } - if (state === "result") { + if (state === "output-available") { if (validated === "rejected") return "Rejected"; return "Executed"; } - if (state === "call") { + if (state === "input-available") { if (validated === "pending") return "Running"; if (validated === "accepted") return "Validated"; if (validated === "rejected") return "Rejected"; diff --git a/frontend/src/components/sidebar/search-popover.tsx b/frontend/src/components/sidebar/search-popover.tsx index 0c07e908d..e89fef38e 100644 --- a/frontend/src/components/sidebar/search-popover.tsx +++ b/frontend/src/components/sidebar/search-popover.tsx @@ -123,7 +123,6 @@ export function SearchPopover({ debounceMs = 400 }: SearchPopoverProps) { })) as SearchMessagesList; setResults(data.result_list ?? []); - console.log(results); } catch (err) { if ((err as Error).name !== "AbortError") { console.error("Search error:", err); diff --git a/frontend/src/components/sidebar/thread-list-client.tsx b/frontend/src/components/sidebar/thread-list-client.tsx index 3f3c5ab1d..728a0ed08 100644 --- a/frontend/src/components/sidebar/thread-list-client.tsx +++ b/frontend/src/components/sidebar/thread-list-client.tsx @@ -33,24 +33,31 @@ export function ThreadListClient({ // Observer to load additional threads. useEffect(() => { + const sentinel = bottomSentinelRef.current; + const scrollContainer = scrollContainerRef.current; + + if (!sentinel || !scrollContainer) return; + observerRef.current = new IntersectionObserver( (entries) => { - if (entries[0].isIntersecting && !isFetchingNextPage) { - if (!hasNextPage) return; - fetchNextPage(); // If the sentinel is visible, load next page + if (entries[0].isIntersecting && hasNextPage && !isFetchingNextPage) { + fetchNextPage(); } }, { - root: scrollContainerRef.current, + root: scrollContainer, + rootMargin: "100px", + threshold: 0.1, }, ); - const sentinel = bottomSentinelRef.current; - if (sentinel && observerRef.current) observerRef.current.observe(sentinel); + + observerRef.current.observe(sentinel); return () => { - if (sentinel && observerRef.current) - observerRef.current.unobserve(sentinel); - if (observerRef.current) observerRef.current.disconnect(); + if (observerRef.current) { + observerRef.current.disconnect(); + observerRef.current = null; + } }; }, [hasNextPage, isFetchingNextPage, fetchNextPage]); @@ -71,9 +78,7 @@ export function ThreadListClient({
)} - {hasNextPage && ( -
- )} + {hasNextPage &&
}
); } diff --git a/frontend/src/hooks/tools.ts b/frontend/src/hooks/tools.ts index 0f39c048b..bf85a083d 100644 --- a/frontend/src/hooks/tools.ts +++ b/frontend/src/hooks/tools.ts @@ -12,14 +12,12 @@ export function useExecuteTool() { threadId: string; toolCallId: string; validation: "accepted" | "rejected"; - args?: string; feedback?: string; } >({ - mutationFn: ({ threadId, toolCallId, validation, args, feedback }) => { + mutationFn: ({ threadId, toolCallId, validation, feedback }) => { const body: BExecuteToolCallRequest = { validation, - args, feedback, }; return fetcher({ diff --git a/frontend/src/lib/auth.ts b/frontend/src/lib/auth.ts index 61ea790ea..17c025d19 100644 --- a/frontend/src/lib/auth.ts +++ b/frontend/src/lib/auth.ts @@ -99,6 +99,9 @@ export const authOptions: NextAuthOptions = { }; async function refreshAccessToken(token: TokenSet) { + if (!env.KEYCLOAK_ID || !env.KEYCLOAK_SECRET) { + throw new Error("Keycloak credentials are not configured"); + } try { const response = await fetch( `${env.KEYCLOAK_ISSUER}/protocol/openid-connect/token`, diff --git a/frontend/src/lib/env.ts b/frontend/src/lib/env.ts index cacbc2d11..63e31403e 100644 --- a/frontend/src/lib/env.ts +++ b/frontend/src/lib/env.ts @@ -1,26 +1,10 @@ -import { createEnv } from "@t3-oss/env-nextjs"; -import { z } from "zod"; - -export const env = createEnv({ - server: { - SERVER_SIDE_BACKEND_URL: z.string().url().optional(), - NEXTAUTH_SECRET: z.string().min(1), - KEYCLOAK_ID: z.string().min(1), - KEYCLOAK_SECRET: z.string().min(1), - KEYCLOAK_ISSUER: z.string().url(), - }, - client: { - NEXT_PUBLIC_BACKEND_URL: z.string().url(), - }, - runtimeEnv: { - // Server vars - SERVER_SIDE_BACKEND_URL: process.env.SERVER_SIDE_BACKEND_URL, - NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET, - KEYCLOAK_ID: process.env.KEYCLOAK_ID, - KEYCLOAK_SECRET: process.env.KEYCLOAK_SECRET, - KEYCLOAK_ISSUER: process.env.KEYCLOAK_ISSUER, - // Client vars - NEXT_PUBLIC_BACKEND_URL: process.env.NEXT_PUBLIC_BACKEND_URL, - }, - skipValidation: true, -}); +export const env = { + // Server + SERVER_SIDE_BACKEND_URL: process.env.SERVER_SIDE_BACKEND_URL, + NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET, + KEYCLOAK_ID: process.env.KEYCLOAK_ID, + KEYCLOAK_SECRET: process.env.KEYCLOAK_SECRET, + KEYCLOAK_ISSUER: process.env.KEYCLOAK_ISSUER, + // Client + NEXT_PUBLIC_BACKEND_URL: process.env.NEXT_PUBLIC_BACKEND_URL, +}; diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index d41d9576d..d7851a8d9 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -1,9 +1,11 @@ import { ReasoningUIPart, TextUIPart, - ToolInvocationUIPart, + ToolUIPart, UIMessage, -} from "@ai-sdk/ui-utils"; + UITools, + UIDataTypes, +} from "ai"; import { components } from "./neuroagent_types"; export type BPaginatedResponseThread = @@ -22,11 +24,12 @@ export type Thread = { title: string; }; -export type Annotation = { - messageId?: string; - toolCallId?: string; - validated?: "accepted" | "rejected" | "pending" | "not_required"; - isComplete?: boolean; +export type MessageMetadata = { + toolCalls: { + toolCallId?: string; + validated?: "accepted" | "rejected" | "pending" | "not_required"; + isComplete?: boolean; + }[]; }; export type BTextPart = components["schemas"]["TextPartVercel"]; @@ -36,9 +39,9 @@ export type BMessageUser = { id: string; role: "user"; createdAt: Date; - content: string; parts: []; - annotation: []; + metadata: undefined; + isComplete: boolean; }; // This type needs to use native vercel AI types which are not defined the backend @@ -46,17 +49,20 @@ export type BMessageAIContent = { id: string; role: "assistant"; createdAt: Date; - content: string; - parts: (TextUIPart | ToolInvocationUIPart | ReasoningUIPart)[]; - annotations: Annotation[]; + parts: (TextUIPart | ToolUIPart | ReasoningUIPart)[]; + metadata: MessageMetadata; + isComplete: boolean; }; export type BMessage = BMessageUser | BMessageAIContent; -// This explicitly overrides any existing 'annotations' property -// The AI SDK make it more general by JSONValue[], but we need to be more specific -export type MessageStrict = Omit & { - annotations?: Annotation[]; +// Extends the type of UIMessage from Vercel AI. +export type MessageStrict< + DATA_PARTS extends UIDataTypes = UIDataTypes, + TOOLS extends UITools = UITools, +> = Omit, "metadata"> & { + metadata?: MessageMetadata; + isComplete: boolean; }; export type BExecuteToolCallRequest = diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts index 6e5b08344..e04c146de 100644 --- a/frontend/src/lib/utils.ts +++ b/frontend/src/lib/utils.ts @@ -1,7 +1,7 @@ import { clsx, type ClassValue } from "clsx"; import { twMerge } from "tailwind-merge"; -import { MessageStrict, Annotation } from "@/lib/types"; -import { ToolInvocation, ToolInvocationUIPart } from "@ai-sdk/ui-utils"; +import { MessageStrict, MessageMetadata } from "@/lib/types"; +import { UIMessagePart, ToolUIPart, UITools, UIDataTypes, UIMessage } from "ai"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); @@ -31,55 +31,60 @@ function safeParse(str: string): T | string { } } +// check if the part od the message is a tool. +export function isToolPart< + DATA_TYPES extends UIDataTypes, + TOOLS extends UITools, +>(part: UIMessagePart): part is ToolUIPart { + return part.type.startsWith("tool-"); +} + // Small utility function to check if the last message has incomplete parts export function isLastMessageComplete(messages: MessageStrict | undefined) { - const annotations = messages?.annotations; - if (annotations?.length === 0) { + const metadata = messages?.metadata; + if (metadata?.toolCalls.length === 0) { return true; } - const hasIncomplete = annotations?.some( - (ann) => "isComplete" in ann && ann.isComplete === false, + const hasIncomplete = metadata?.toolCalls?.some( + (met) => "isComplete" in met && met.isComplete === false, ); return !hasIncomplete; } +// Util to get the last text part +export function getLastText(message: MessageStrict | undefined): string { + return message?.parts.findLast((e) => e.type == "text")?.text || ""; +} + // Utils to get all tool calls from an AI message export function getToolInvocations( message: MessageStrict | undefined, -): ToolInvocation[] { +): ToolUIPart[] { return ( message?.parts - ?.filter( - (part): part is ToolInvocationUIPart => - part.type === "tool-invocation" && - typeof part.toolInvocation === "object", - ) - .map((part) => part.toolInvocation) ?? [] + ?.filter((part): part is ToolUIPart => part.type.startsWith("tool-")) + .map((part) => part) ?? [] ); } +// Utils to get the last text part of the Message parts: +export function getLastMessageText(messages: UIMessage[]): string { + return messages.at(-1)?.parts.findLast((e) => e.type === "text")?.text || ""; +} + // Utils to get all storage ID from a single tool call message -export function getStorageID( - toolCall: ToolInvocationUIPart | undefined, -): string[] { - if ( - !toolCall || - toolCall.type !== "tool-invocation" || - !toolCall.toolInvocation - ) { +export function getStorageID(toolCall: ToolUIPart | undefined): string[] { + if (!toolCall || !toolCall.type.startsWith("tool-")) { return []; } - if ( - toolCall.toolInvocation.state !== "result" || - !toolCall.toolInvocation.result - ) { + if (toolCall.state !== "output-available" || !toolCall.output) { return []; } const storageIds: string[] = []; - const rawResult = toolCall.toolInvocation.result; + const rawResult = toolCall.output; try { // If the result is a JSON string, parse it; otherwise assume it's already an object @@ -101,19 +106,38 @@ export function getStorageID( return storageIds; } -// Small utility function that finds the right tool call in annotations and returns its status +// Small utility function that finds the right tool call in metadata and returns its status export function getValidationStatus( - annotations: Annotation[] | undefined, + metadata: MessageMetadata | undefined, toolCallId: string, ) { - const ann = annotations?.find((a) => a.toolCallId === toolCallId); - if (!ann) return undefined; - return ann.validated; + const met = metadata?.toolCalls?.find((a) => a.toolCallId === toolCallId); + if (!met) return undefined; + return met.validated; } -export function getStoppedStatus( - annotations: Annotation[] | undefined, - toolCallId: string, -) { - const ann = annotations?.find((a) => a.toolCallId === toolCallId); - return !(ann?.isComplete ?? true); + +// Util to check if all tools have been executed. +export function lastAssistantHasAllToolOutputs(useChatReturn: { + messages: UIMessage[]; +}) { + if (!Array.isArray(useChatReturn.messages)) return false; + const last = useChatReturn.messages.at(-1); + if (!last || last.role !== "assistant") return false; + + const parts = last.parts ?? []; + const lastToolIndex = parts.findLastIndex((p) => p.type.startsWith("tool-")); + + if (lastToolIndex === -1) return false; + + // Don't auto-send if there's text after the last tool + const hasTextAfterTools = parts + .slice(lastToolIndex + 1) + .some((p) => p.type === "text" && "text" in p && p.text); + + if (hasTextAfterTools) return false; + + // All tools must have outputs + return parts + .filter((p): p is ToolUIPart => p.type.startsWith("tool-")) + .every((p) => p.state === "output-available" || !!p.output); } diff --git a/frontend/src/lib/zod-schemas.ts b/frontend/src/lib/zod-schemas.ts deleted file mode 100644 index 0d5c01e60..000000000 --- a/frontend/src/lib/zod-schemas.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { z } from "zod"; - -export const scsPostSchema = z.object({ - me_model_id: z - .string() - .describe( - "ID of the neuron model to be used in the simulation. The model ID can be fetched using the 'memodelgetall-tool'.", - ), - - current_injection__inject_to: z - .string() - .default("soma[0]") - .describe("Section to inject the current to."), - - current_injection__stimulus__stimulus_type: z - .enum(["current_clamp", "voltage_clamp", "conductance"]) - .default("current_clamp") - .describe("Type of stimulus to be used."), - - current_injection__stimulus__stimulus_protocol: z - .enum(["ap_waveform", "idrest", "iv", "fire_pattern"]) - .default("ap_waveform") - .describe("Stimulus protocol to be used."), - - current_injection__stimulus__amplitudes: z - .array(z.number()) - .min(1) - .default([0.1]) - .describe("List of amplitudes for the stimulus."), - - record_from: z - .array( - z.object({ - section: z - .string() - .default("soma[0]") - .describe("Section to record from."), - offset: z - .number() - .min(0) - .max(1) - .default(0.5) - .describe("Offset in the section to record from."), - }), - ) - .min(1) - .default([{ section: "soma[0]", offset: 0.5 }]) - .describe("List of sections to record from during the simulation."), - - conditions__celsius: z - .number() - .int() - .min(0) - .max(50) - .default(34) - .describe("Temperature in celsius."), - - conditions__vinit: z - .number() - .int() - .default(-73) - .describe("Initial voltage in mV."), - - conditions__hypamp: z - .number() - .int() - .default(0) - .describe("Holding current in nA."), - - conditions__max_time: z - .number() - .int() - .max(3000) - .default(100) - .describe("Maximum simulation time in ms."), - - conditions__time_step: z - .number() - .min(0.001) - .max(10) - .default(0.05) - .describe("Time step in ms."), - - conditions__seed: z.number().int().default(100).describe("Random seed."), -});