django-agent-studio 0.3.3__py3-none-any.whl → 0.3.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_agent_studio/agents/dynamic.py +164 -4
- django_agent_studio/api/serializers.py +27 -8
- django_agent_studio/branding.py +103 -0
- django_agent_studio/static/agent-frontend/chat-widget.css +52 -0
- django_agent_studio/static/agent-frontend/chat-widget.js +222 -206
- django_agent_studio/static/django_agent_studio/js/builder.js +58 -58
- django_agent_studio/static/django_agent_studio/js/builder.js.map +1 -1
- django_agent_studio/static/django_agent_studio/js/style.css +1 -1
- django_agent_studio/templates/django_agent_studio/agent_list.html +31 -28
- django_agent_studio/templates/django_agent_studio/base.html +116 -28
- django_agent_studio/templates/django_agent_studio/builder.html +1 -1
- django_agent_studio/templates/django_agent_studio/collaborators.html +83 -95
- django_agent_studio/templates/django_agent_studio/home.html +66 -48
- django_agent_studio/templates/django_agent_studio/system_create.html +22 -24
- django_agent_studio/templates/django_agent_studio/system_list.html +38 -30
- django_agent_studio/templates/django_agent_studio/system_test.html +27 -21
- django_agent_studio/templates/django_agent_studio/test.html +22 -17
- {django_agent_studio-0.3.3.dist-info → django_agent_studio-0.3.4.dist-info}/METADATA +1 -1
- {django_agent_studio-0.3.3.dist-info → django_agent_studio-0.3.4.dist-info}/RECORD +22 -21
- {django_agent_studio-0.3.3.dist-info → django_agent_studio-0.3.4.dist-info}/WHEEL +0 -0
- {django_agent_studio-0.3.3.dist-info → django_agent_studio-0.3.4.dist-info}/licenses/LICENSE +0 -0
- {django_agent_studio-0.3.3.dist-info → django_agent_studio-0.3.4.dist-info}/top_level.txt +0 -0
|
@@ -626,8 +626,13 @@ class DynamicAgentRuntime(AgentRuntime):
|
|
|
626
626
|
return ""
|
|
627
627
|
|
|
628
628
|
def _build_tool_schemas(self, config: dict) -> list:
|
|
629
|
-
"""Build OpenAI-format tool schemas from config.
|
|
629
|
+
"""Build OpenAI-format tool schemas from config.
|
|
630
|
+
|
|
631
|
+
Includes both regular tools and sub-agent tools.
|
|
632
|
+
"""
|
|
630
633
|
schemas = []
|
|
634
|
+
|
|
635
|
+
# Add regular tools
|
|
631
636
|
for tool in config.get("tools", []):
|
|
632
637
|
# Skip the _meta field when building schema
|
|
633
638
|
schema = {
|
|
@@ -635,22 +640,68 @@ class DynamicAgentRuntime(AgentRuntime):
|
|
|
635
640
|
"function": tool.get("function", {}),
|
|
636
641
|
}
|
|
637
642
|
schemas.append(schema)
|
|
643
|
+
|
|
644
|
+
# Add sub-agent tools in OpenAI format
|
|
645
|
+
for sub_tool in config.get("sub_agent_tools", []):
|
|
646
|
+
schema = {
|
|
647
|
+
"type": "function",
|
|
648
|
+
"function": {
|
|
649
|
+
"name": sub_tool.get("name"),
|
|
650
|
+
"description": sub_tool.get("description", ""),
|
|
651
|
+
"parameters": {
|
|
652
|
+
"type": "object",
|
|
653
|
+
"properties": {
|
|
654
|
+
"message": {
|
|
655
|
+
"type": "string",
|
|
656
|
+
"description": f"The message or task to send to the {sub_tool.get('name')} agent",
|
|
657
|
+
},
|
|
658
|
+
"context": {
|
|
659
|
+
"type": "string",
|
|
660
|
+
"description": "Optional additional context to include",
|
|
661
|
+
},
|
|
662
|
+
},
|
|
663
|
+
"required": ["message"],
|
|
664
|
+
},
|
|
665
|
+
},
|
|
666
|
+
}
|
|
667
|
+
schemas.append(schema)
|
|
668
|
+
logger.debug(f"Added sub-agent tool schema: {sub_tool.get('name')}")
|
|
669
|
+
|
|
638
670
|
return schemas
|
|
639
671
|
|
|
640
672
|
def _build_tool_map(self, config: dict) -> dict:
|
|
641
|
-
"""Build a map of tool name to execution info.
|
|
673
|
+
"""Build a map of tool name to execution info.
|
|
674
|
+
|
|
675
|
+
Includes both regular tools and sub-agent tools.
|
|
676
|
+
"""
|
|
642
677
|
tool_map = {}
|
|
678
|
+
|
|
679
|
+
# Add regular tools
|
|
643
680
|
for tool in config.get("tools", []):
|
|
644
681
|
func_info = tool.get("function", {})
|
|
645
682
|
tool_name = func_info.get("name")
|
|
646
683
|
if tool_name:
|
|
647
684
|
# Get execution metadata from _meta field
|
|
648
685
|
meta = tool.get("_meta", {})
|
|
686
|
+
# Check both function_path (DynamicTool) and builtin_ref (AgentTool)
|
|
649
687
|
tool_map[tool_name] = {
|
|
650
|
-
"function_path": meta.get("function_path"),
|
|
688
|
+
"function_path": meta.get("function_path") or meta.get("builtin_ref"),
|
|
651
689
|
"tool_id": meta.get("tool_id"),
|
|
652
690
|
"is_dynamic": meta.get("is_dynamic", False),
|
|
691
|
+
"is_sub_agent": False,
|
|
653
692
|
}
|
|
693
|
+
|
|
694
|
+
# Add sub-agent tools
|
|
695
|
+
for sub_tool in config.get("sub_agent_tools", []):
|
|
696
|
+
tool_name = sub_tool.get("name")
|
|
697
|
+
if tool_name:
|
|
698
|
+
tool_map[tool_name] = {
|
|
699
|
+
"is_sub_agent": True,
|
|
700
|
+
"agent_slug": sub_tool.get("agent_slug"),
|
|
701
|
+
"context_mode": sub_tool.get("context_mode", "message_only"),
|
|
702
|
+
}
|
|
703
|
+
logger.debug(f"Added sub-agent tool to map: {tool_name} -> {sub_tool.get('agent_slug')}")
|
|
704
|
+
|
|
654
705
|
return tool_map
|
|
655
706
|
|
|
656
707
|
async def _execute_tool(
|
|
@@ -661,8 +712,22 @@ class DynamicAgentRuntime(AgentRuntime):
|
|
|
661
712
|
executor: "DynamicToolExecutor",
|
|
662
713
|
ctx: RunContext,
|
|
663
714
|
) -> str:
|
|
664
|
-
"""Execute a tool and return its result.
|
|
715
|
+
"""Execute a tool and return its result.
|
|
716
|
+
|
|
717
|
+
Handles both regular tools and sub-agent tools.
|
|
718
|
+
"""
|
|
665
719
|
tool_info = tool_map.get(tool_name, {})
|
|
720
|
+
|
|
721
|
+
# Check if this is a sub-agent tool
|
|
722
|
+
if tool_info.get("is_sub_agent"):
|
|
723
|
+
return await self._execute_sub_agent_tool(
|
|
724
|
+
tool_name=tool_name,
|
|
725
|
+
tool_args=tool_args,
|
|
726
|
+
tool_info=tool_info,
|
|
727
|
+
ctx=ctx,
|
|
728
|
+
)
|
|
729
|
+
|
|
730
|
+
# Regular tool execution
|
|
666
731
|
function_path = tool_info.get("function_path")
|
|
667
732
|
|
|
668
733
|
if not function_path:
|
|
@@ -690,3 +755,98 @@ class DynamicAgentRuntime(AgentRuntime):
|
|
|
690
755
|
logger.exception(f"Error executing tool {tool_name}")
|
|
691
756
|
return json.dumps({"error": str(e)})
|
|
692
757
|
|
|
758
|
+
async def _execute_sub_agent_tool(
|
|
759
|
+
self,
|
|
760
|
+
tool_name: str,
|
|
761
|
+
tool_args: dict,
|
|
762
|
+
tool_info: dict,
|
|
763
|
+
ctx: RunContext,
|
|
764
|
+
) -> str:
|
|
765
|
+
"""Execute a sub-agent tool by invoking the sub-agent.
|
|
766
|
+
|
|
767
|
+
Args:
|
|
768
|
+
tool_name: Name of the sub-agent tool
|
|
769
|
+
tool_args: Arguments passed to the tool (message, context)
|
|
770
|
+
tool_info: Tool metadata including agent_slug and context_mode
|
|
771
|
+
ctx: Parent agent's run context
|
|
772
|
+
|
|
773
|
+
Returns:
|
|
774
|
+
JSON string with the sub-agent's response
|
|
775
|
+
"""
|
|
776
|
+
from agent_runtime_core.multi_agent import (
|
|
777
|
+
AgentTool as AgentToolCore,
|
|
778
|
+
InvocationMode,
|
|
779
|
+
ContextMode,
|
|
780
|
+
invoke_agent,
|
|
781
|
+
)
|
|
782
|
+
from django_agent_runtime.runtime.registry import get_runtime_async
|
|
783
|
+
|
|
784
|
+
sub_agent_slug = tool_info.get("agent_slug")
|
|
785
|
+
context_mode_str = tool_info.get("context_mode", "message_only")
|
|
786
|
+
|
|
787
|
+
# Map context_mode string to enum
|
|
788
|
+
context_mode_map = {
|
|
789
|
+
'message_only': ContextMode.MESSAGE_ONLY,
|
|
790
|
+
'summary': ContextMode.SUMMARY,
|
|
791
|
+
'full': ContextMode.FULL,
|
|
792
|
+
}
|
|
793
|
+
context_mode = context_mode_map.get(context_mode_str, ContextMode.MESSAGE_ONLY)
|
|
794
|
+
|
|
795
|
+
message = tool_args.get("message", "")
|
|
796
|
+
additional_context = tool_args.get("context")
|
|
797
|
+
|
|
798
|
+
if not message:
|
|
799
|
+
return json.dumps({"error": "Missing required parameter: message"})
|
|
800
|
+
|
|
801
|
+
try:
|
|
802
|
+
# Get the sub-agent's runtime
|
|
803
|
+
sub_agent_runtime = await get_runtime_async(sub_agent_slug)
|
|
804
|
+
|
|
805
|
+
# Create an AgentTool wrapper for the sub-agent
|
|
806
|
+
agent_tool = AgentToolCore(
|
|
807
|
+
agent=sub_agent_runtime,
|
|
808
|
+
name=tool_name,
|
|
809
|
+
description=f"Sub-agent: {sub_agent_slug}",
|
|
810
|
+
invocation_mode=InvocationMode.DELEGATE,
|
|
811
|
+
context_mode=context_mode,
|
|
812
|
+
metadata={
|
|
813
|
+
'sub_agent_slug': sub_agent_slug,
|
|
814
|
+
'parent_run_id': str(ctx.run_id) if ctx.run_id else None,
|
|
815
|
+
},
|
|
816
|
+
)
|
|
817
|
+
|
|
818
|
+
# Get conversation history from parent context
|
|
819
|
+
conversation_history = list(ctx.input_messages)
|
|
820
|
+
|
|
821
|
+
# Invoke the sub-agent
|
|
822
|
+
logger.info(f"Invoking sub-agent '{sub_agent_slug}' via tool '{tool_name}'")
|
|
823
|
+
result = await invoke_agent(
|
|
824
|
+
agent_tool=agent_tool,
|
|
825
|
+
message=message,
|
|
826
|
+
parent_ctx=ctx,
|
|
827
|
+
conversation_history=conversation_history,
|
|
828
|
+
additional_context=additional_context,
|
|
829
|
+
)
|
|
830
|
+
|
|
831
|
+
logger.info(f"Sub-agent '{sub_agent_slug}' completed for tool '{tool_name}'")
|
|
832
|
+
|
|
833
|
+
return json.dumps({
|
|
834
|
+
"response": result.response,
|
|
835
|
+
"sub_agent": result.sub_agent_key,
|
|
836
|
+
"handoff": result.handoff,
|
|
837
|
+
})
|
|
838
|
+
|
|
839
|
+
except KeyError as e:
|
|
840
|
+
logger.error(f"Sub-agent not found: {sub_agent_slug} - {e}")
|
|
841
|
+
return json.dumps({
|
|
842
|
+
"error": f"Sub-agent not found: {sub_agent_slug}",
|
|
843
|
+
"tool": tool_name,
|
|
844
|
+
})
|
|
845
|
+
except Exception as e:
|
|
846
|
+
logger.exception(f"Error invoking sub-agent '{sub_agent_slug}'")
|
|
847
|
+
return json.dumps({
|
|
848
|
+
"error": str(e),
|
|
849
|
+
"tool": tool_name,
|
|
850
|
+
"sub_agent": sub_agent_slug,
|
|
851
|
+
})
|
|
852
|
+
|
|
@@ -503,8 +503,8 @@ class AgentSystemVersionSerializer(serializers.ModelSerializer):
|
|
|
503
503
|
class AgentSystemListSerializer(serializers.ModelSerializer):
|
|
504
504
|
"""Serializer for listing AgentSystems."""
|
|
505
505
|
|
|
506
|
-
|
|
507
|
-
|
|
506
|
+
entry_agent = serializers.SerializerMethodField()
|
|
507
|
+
members = AgentSystemMemberSerializer(many=True, read_only=True)
|
|
508
508
|
member_count = serializers.SerializerMethodField()
|
|
509
509
|
active_version = serializers.SerializerMethodField()
|
|
510
510
|
|
|
@@ -516,8 +516,7 @@ class AgentSystemListSerializer(serializers.ModelSerializer):
|
|
|
516
516
|
"name",
|
|
517
517
|
"description",
|
|
518
518
|
"entry_agent",
|
|
519
|
-
"
|
|
520
|
-
"entry_agent_slug",
|
|
519
|
+
"members",
|
|
521
520
|
"is_active",
|
|
522
521
|
"member_count",
|
|
523
522
|
"active_version",
|
|
@@ -526,6 +525,16 @@ class AgentSystemListSerializer(serializers.ModelSerializer):
|
|
|
526
525
|
]
|
|
527
526
|
read_only_fields = ["id", "created_at", "updated_at"]
|
|
528
527
|
|
|
528
|
+
def get_entry_agent(self, obj):
|
|
529
|
+
if obj.entry_agent:
|
|
530
|
+
return {
|
|
531
|
+
"id": str(obj.entry_agent.id),
|
|
532
|
+
"name": obj.entry_agent.name,
|
|
533
|
+
"slug": obj.entry_agent.slug,
|
|
534
|
+
"icon": obj.entry_agent.icon,
|
|
535
|
+
}
|
|
536
|
+
return None
|
|
537
|
+
|
|
529
538
|
def get_member_count(self, obj):
|
|
530
539
|
return obj.members.count()
|
|
531
540
|
|
|
@@ -539,8 +548,6 @@ class AgentSystemListSerializer(serializers.ModelSerializer):
|
|
|
539
548
|
class AgentSystemDetailSerializer(serializers.ModelSerializer):
|
|
540
549
|
"""Serializer for AgentSystem detail view."""
|
|
541
550
|
|
|
542
|
-
entry_agent_name = serializers.CharField(source='entry_agent.name', read_only=True)
|
|
543
|
-
entry_agent_slug = serializers.CharField(source='entry_agent.slug', read_only=True)
|
|
544
551
|
members = AgentSystemMemberSerializer(many=True, read_only=True)
|
|
545
552
|
versions = AgentSystemVersionSerializer(many=True, read_only=True)
|
|
546
553
|
dependency_graph = serializers.SerializerMethodField()
|
|
@@ -553,8 +560,6 @@ class AgentSystemDetailSerializer(serializers.ModelSerializer):
|
|
|
553
560
|
"name",
|
|
554
561
|
"description",
|
|
555
562
|
"entry_agent",
|
|
556
|
-
"entry_agent_name",
|
|
557
|
-
"entry_agent_slug",
|
|
558
563
|
"is_active",
|
|
559
564
|
"members",
|
|
560
565
|
"versions",
|
|
@@ -567,6 +572,20 @@ class AgentSystemDetailSerializer(serializers.ModelSerializer):
|
|
|
567
572
|
def get_dependency_graph(self, obj):
|
|
568
573
|
return obj.get_dependency_graph()
|
|
569
574
|
|
|
575
|
+
def to_representation(self, instance):
|
|
576
|
+
"""Return entry_agent as an object for reading."""
|
|
577
|
+
data = super().to_representation(instance)
|
|
578
|
+
if instance.entry_agent:
|
|
579
|
+
data['entry_agent'] = {
|
|
580
|
+
"id": str(instance.entry_agent.id),
|
|
581
|
+
"name": instance.entry_agent.name,
|
|
582
|
+
"slug": instance.entry_agent.slug,
|
|
583
|
+
"icon": instance.entry_agent.icon,
|
|
584
|
+
}
|
|
585
|
+
else:
|
|
586
|
+
data['entry_agent'] = None
|
|
587
|
+
return data
|
|
588
|
+
|
|
570
589
|
|
|
571
590
|
class AgentSystemCreateSerializer(serializers.Serializer):
|
|
572
591
|
"""Serializer for creating an AgentSystem."""
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Branding configuration for Django Agent Studio.
|
|
3
|
+
|
|
4
|
+
This module provides customizable branding settings that can be overridden
|
|
5
|
+
in Django settings via the AGENT_STUDIO_BRANDING dictionary.
|
|
6
|
+
|
|
7
|
+
Example settings.py configuration:
|
|
8
|
+
|
|
9
|
+
AGENT_STUDIO_BRANDING = {
|
|
10
|
+
'app_name': 'My Agent Studio',
|
|
11
|
+
'logo_svg': '<svg>...</svg>', # Custom logo SVG
|
|
12
|
+
'colors': {
|
|
13
|
+
'primary': '#00142E',
|
|
14
|
+
'accent': '#4fc4f7',
|
|
15
|
+
'secondary': '#253547',
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from django.conf import settings
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# Default branding configuration
|
|
24
|
+
DEFAULT_BRANDING = {
|
|
25
|
+
# Application name
|
|
26
|
+
'app_name': 'Agent Studio',
|
|
27
|
+
|
|
28
|
+
# Logo - SVG markup (displayed in header)
|
|
29
|
+
# Default is a simple "AS" text logo
|
|
30
|
+
'logo_svg': '''
|
|
31
|
+
<svg viewBox="0 0 80 32" fill="none" xmlns="http://www.w3.org/2000/svg" class="h-7">
|
|
32
|
+
<text x="0" y="24" fill="currentColor" font-family="system-ui, -apple-system, sans-serif" font-size="24" font-weight="700">AS</text>
|
|
33
|
+
</svg>
|
|
34
|
+
''',
|
|
35
|
+
|
|
36
|
+
# Whether to show app name next to logo
|
|
37
|
+
'show_app_name': True,
|
|
38
|
+
|
|
39
|
+
# Colors - these are the defaults (professional blue/cyan theme)
|
|
40
|
+
'colors': {
|
|
41
|
+
# Primary color - used for header, primary buttons, main text
|
|
42
|
+
'primary': '#00142E',
|
|
43
|
+
'primary_light': '#0a2540',
|
|
44
|
+
'primary_dark': '#000d1f',
|
|
45
|
+
|
|
46
|
+
# Accent color - used for highlights, focus rings, CTAs
|
|
47
|
+
'accent': '#4fc4f7',
|
|
48
|
+
'accent_light': '#7dd3fc',
|
|
49
|
+
'accent_dark': '#3db8eb',
|
|
50
|
+
|
|
51
|
+
# Secondary color - used for secondary backgrounds, gradients
|
|
52
|
+
'secondary': '#253547',
|
|
53
|
+
'secondary_light': '#334155',
|
|
54
|
+
'secondary_dark': '#1e293b',
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
# Chat widget primary color (used by agent-frontend)
|
|
58
|
+
'chat_primary_color': '#00142E',
|
|
59
|
+
|
|
60
|
+
# Custom CSS to inject (optional)
|
|
61
|
+
'custom_css': '',
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def get_branding():
|
|
66
|
+
"""
|
|
67
|
+
Get the merged branding configuration.
|
|
68
|
+
|
|
69
|
+
Merges user settings from AGENT_STUDIO_BRANDING with defaults.
|
|
70
|
+
"""
|
|
71
|
+
user_branding = getattr(settings, 'AGENT_STUDIO_BRANDING', {})
|
|
72
|
+
|
|
73
|
+
# Deep merge colors
|
|
74
|
+
merged = DEFAULT_BRANDING.copy()
|
|
75
|
+
|
|
76
|
+
for key, value in user_branding.items():
|
|
77
|
+
if key == 'colors' and isinstance(value, dict):
|
|
78
|
+
# Merge colors dict
|
|
79
|
+
merged['colors'] = {**DEFAULT_BRANDING['colors'], **value}
|
|
80
|
+
else:
|
|
81
|
+
merged[key] = value
|
|
82
|
+
|
|
83
|
+
return merged
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def branding_context_processor(request):
|
|
87
|
+
"""
|
|
88
|
+
Django context processor that adds branding to all templates.
|
|
89
|
+
|
|
90
|
+
Add to settings.py TEMPLATES['OPTIONS']['context_processors']:
|
|
91
|
+
'django_agent_studio.branding.branding_context_processor',
|
|
92
|
+
"""
|
|
93
|
+
branding = get_branding()
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
'studio_branding': branding,
|
|
97
|
+
'studio_app_name': branding['app_name'],
|
|
98
|
+
'studio_logo_svg': branding['logo_svg'],
|
|
99
|
+
'studio_show_app_name': branding['show_app_name'],
|
|
100
|
+
'studio_colors': branding['colors'],
|
|
101
|
+
'studio_chat_primary_color': branding['chat_primary_color'],
|
|
102
|
+
'studio_custom_css': branding['custom_css'],
|
|
103
|
+
}
|
|
@@ -89,6 +89,7 @@
|
|
|
89
89
|
|
|
90
90
|
/* Widget container */
|
|
91
91
|
.cw-widget {
|
|
92
|
+
position: relative;
|
|
92
93
|
width: 380px;
|
|
93
94
|
height: 500px;
|
|
94
95
|
background: var(--cw-bg);
|
|
@@ -1272,6 +1273,57 @@
|
|
|
1272
1273
|
margin-top: 2px;
|
|
1273
1274
|
}
|
|
1274
1275
|
|
|
1276
|
+
/* Thinking Toggle */
|
|
1277
|
+
.cw-model-selector {
|
|
1278
|
+
display: flex;
|
|
1279
|
+
align-items: center;
|
|
1280
|
+
gap: 8px;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
.cw-model-selector > .cw-model-btn {
|
|
1284
|
+
flex: 1;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
.cw-thinking-toggle {
|
|
1288
|
+
display: flex;
|
|
1289
|
+
align-items: center;
|
|
1290
|
+
justify-content: center;
|
|
1291
|
+
width: 36px;
|
|
1292
|
+
height: 36px;
|
|
1293
|
+
padding: 0;
|
|
1294
|
+
border: 1px solid var(--cw-border);
|
|
1295
|
+
border-radius: var(--cw-radius-sm);
|
|
1296
|
+
background: var(--cw-bg);
|
|
1297
|
+
cursor: pointer;
|
|
1298
|
+
transition: all 0.2s ease;
|
|
1299
|
+
flex-shrink: 0;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
.cw-thinking-toggle:hover {
|
|
1303
|
+
border-color: var(--cw-primary);
|
|
1304
|
+
background: var(--cw-bg-muted);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
.cw-thinking-toggle.cw-thinking-enabled {
|
|
1308
|
+
background: rgba(0, 102, 204, 0.15);
|
|
1309
|
+
border-color: var(--cw-primary);
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
.cw-thinking-toggle:disabled {
|
|
1313
|
+
opacity: 0.5;
|
|
1314
|
+
cursor: not-allowed;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
.cw-thinking-icon {
|
|
1318
|
+
font-size: 18px;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
.cw-thinking-badge {
|
|
1322
|
+
margin-left: 4px;
|
|
1323
|
+
font-size: 12px;
|
|
1324
|
+
opacity: 0.7;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1275
1327
|
/* ============================================================================
|
|
1276
1328
|
Tool Call Cards (Claude-style)
|
|
1277
1329
|
============================================================================ */
|