Zach Wentz commited on
Commit
d321359
·
1 Parent(s): 7843e97

🤖 Deploy chat_env environment - 2025-10-19 23:03:44

Browse files
Files changed (1) hide show
  1. src/core/env_server/web_interface.py +279 -21
src/core/env_server/web_interface.py CHANGED
@@ -244,7 +244,15 @@ def create_web_interface_app(
244
  @app.post("/web/step")
245
  async def web_step(request: Dict[str, Any]):
246
  """Step endpoint for web interface."""
247
- action_data = request.get("action", {})
 
 
 
 
 
 
 
 
248
  return await web_manager.step_environment(action_data)
249
 
250
  @app.get("/web/state")
@@ -258,6 +266,14 @@ def create_web_interface_app(
258
  def get_web_interface_html(action_cls: Type[Action]) -> str:
259
  """Generate the HTML for the web interface."""
260
 
 
 
 
 
 
 
 
 
261
  # Get action fields for dynamic form generation
262
  action_fields = []
263
  if hasattr(action_cls, '__dataclass_fields__'):
@@ -500,6 +516,103 @@ def get_web_interface_html(action_cls: Type[Action]) -> str:
500
  max-height: 200px;
501
  overflow-y: auto;
502
  }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
503
  </style>
504
  </head>
505
  <body>
@@ -511,14 +624,8 @@ def get_web_interface_html(action_cls: Type[Action]) -> str:
511
  HumanAgent Interface
512
  </div>
513
  <div class="pane-content">
514
- <!-- Action Form -->
515
- <div class="action-form">
516
- <h3>Take Action</h3>
517
- <form id="action-form">
518
- {_generate_action_form_fields(action_fields)}
519
- <button type="submit" class="btn" id="step-btn">Step</button>
520
- </form>
521
- </div>
522
 
523
  <!-- Control Buttons -->
524
  <div style="margin-bottom: 20px;">
@@ -618,11 +725,32 @@ def get_web_interface_html(action_cls: Type[Action]) -> str:
618
  }}
619
 
620
  setupEventListeners() {{
621
- // Action form submission
622
- document.getElementById('action-form').addEventListener('submit', (e) => {{
623
- e.preventDefault();
624
- this.submitAction();
625
- }});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
626
 
627
  // Reset button
628
  document.getElementById('reset-btn').addEventListener('click', () => {{
@@ -635,6 +763,61 @@ def get_web_interface_html(action_cls: Type[Action]) -> str:
635
  }});
636
  }}
637
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
638
  async submitAction() {{
639
  const formData = new FormData(document.getElementById('action-form'));
640
  const action = {{}};
@@ -716,6 +899,9 @@ def get_web_interface_html(action_cls: Type[Action]) -> str:
716
  }}
717
 
718
  updateUI(episodeState) {{
 
 
 
719
  // Update current state
720
  document.getElementById('env-status').textContent =
721
  episodeState.is_reset ? 'Reset' : 'Running';
@@ -724,14 +910,19 @@ def get_web_interface_html(action_cls: Type[Action]) -> str:
724
  document.getElementById('step-count').textContent =
725
  episodeState.step_count.toString();
726
 
727
- // Update current observation
728
- const observationDiv = document.getElementById('current-observation');
729
- if (episodeState.current_observation) {{
730
- observationDiv.textContent = JSON.stringify(
731
- episodeState.current_observation, null, 2
732
- );
733
  }} else {{
734
- observationDiv.textContent = 'No observation yet';
 
 
 
 
 
 
 
 
735
  }}
736
 
737
  // Update action logs
@@ -752,6 +943,25 @@ def get_web_interface_html(action_cls: Type[Action]) -> str:
752
  `).join('');
753
  }}
754
  }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
755
  }}
756
 
757
  // Initialize the web interface when the page loads
@@ -764,6 +974,54 @@ def get_web_interface_html(action_cls: Type[Action]) -> str:
764
  """.replace('{_generate_action_form_fields(action_fields)}', _generate_action_form_fields(action_fields))
765
 
766
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
767
  def _generate_action_form_fields(action_fields: List[Dict[str, Any]]) -> str:
768
  """Generate HTML form fields for action input."""
769
  if not action_fields:
 
244
  @app.post("/web/step")
245
  async def web_step(request: Dict[str, Any]):
246
  """Step endpoint for web interface."""
247
+ # Check if this is a message-based request (chat environment)
248
+ if "message" in request:
249
+ message = request["message"]
250
+ # Convert message to action using the environment's message_to_action method
251
+ action = web_manager.env.message_to_action(message)
252
+ action_data = {"tokens": action.tokens.tolist()}
253
+ else:
254
+ action_data = request.get("action", {})
255
+
256
  return await web_manager.step_environment(action_data)
257
 
258
  @app.get("/web/state")
 
266
  def get_web_interface_html(action_cls: Type[Action]) -> str:
267
  """Generate the HTML for the web interface."""
268
 
269
+ # Check if this is a chat environment by looking for tokens field
270
+ is_chat_env = False
271
+ if hasattr(action_cls, '__dataclass_fields__'):
272
+ for field_name, field_info in action_cls.__dataclass_fields__.items():
273
+ if field_name == 'tokens' and hasattr(field_info.type, '__name__') and 'Tensor' in field_info.type.__name__:
274
+ is_chat_env = True
275
+ break
276
+
277
  # Get action fields for dynamic form generation
278
  action_fields = []
279
  if hasattr(action_cls, '__dataclass_fields__'):
 
516
  max-height: 200px;
517
  overflow-y: auto;
518
  }}
519
+
520
+ /* Chat Interface Styles */
521
+ .chat-interface {{
522
+ background: white;
523
+ border: 1px solid #e0e0e0;
524
+ border-radius: 8px;
525
+ padding: 20px;
526
+ margin-bottom: 20px;
527
+ }}
528
+
529
+ .chat-messages {{
530
+ background: #f8f9fa;
531
+ border: 1px solid #e0e0e0;
532
+ border-radius: 8px;
533
+ padding: 15px;
534
+ margin-bottom: 15px;
535
+ max-height: 400px;
536
+ overflow-y: auto;
537
+ }}
538
+
539
+ .chat-message {{
540
+ margin-bottom: 15px;
541
+ padding: 10px;
542
+ border-radius: 8px;
543
+ }}
544
+
545
+ .chat-message:last-child {{
546
+ margin-bottom: 0;
547
+ }}
548
+
549
+ .chat-message.user {{
550
+ background: #e3f2fd;
551
+ margin-left: 20px;
552
+ }}
553
+
554
+ .chat-message.assistant {{
555
+ background: #f3e5f5;
556
+ margin-right: 20px;
557
+ }}
558
+
559
+ .chat-message.system {{
560
+ background: #e8f5e8;
561
+ font-style: italic;
562
+ }}
563
+
564
+ .message-role {{
565
+ font-weight: 600;
566
+ font-size: 12px;
567
+ color: #666;
568
+ margin-bottom: 5px;
569
+ }}
570
+
571
+ .message-content {{
572
+ font-size: 14px;
573
+ line-height: 1.4;
574
+ }}
575
+
576
+ .chat-input-container {{
577
+ border-top: 1px solid #e0e0e0;
578
+ padding-top: 15px;
579
+ }}
580
+
581
+ .role-selector {{
582
+ margin-bottom: 10px;
583
+ }}
584
+
585
+ .role-selector label {{
586
+ font-weight: 500;
587
+ margin-right: 10px;
588
+ }}
589
+
590
+ .role-selector select {{
591
+ padding: 5px 10px;
592
+ border: 1px solid #ddd;
593
+ border-radius: 4px;
594
+ }}
595
+
596
+ .message-input {{
597
+ display: flex;
598
+ gap: 10px;
599
+ align-items: flex-end;
600
+ }}
601
+
602
+ .message-input textarea {{
603
+ flex: 1;
604
+ padding: 10px;
605
+ border: 1px solid #ddd;
606
+ border-radius: 4px;
607
+ resize: vertical;
608
+ font-family: inherit;
609
+ }}
610
+
611
+ .message-input textarea:focus {{
612
+ outline: none;
613
+ border-color: #007bff;
614
+ box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
615
+ }}
616
  </style>
617
  </head>
618
  <body>
 
624
  HumanAgent Interface
625
  </div>
626
  <div class="pane-content">
627
+ <!-- Action Form or Chat Interface -->
628
+ {_generate_action_interface(action_fields, is_chat_env)}
 
 
 
 
 
 
629
 
630
  <!-- Control Buttons -->
631
  <div style="margin-bottom: 20px;">
 
725
  }}
726
 
727
  setupEventListeners() {{
728
+ // Check if this is a chat environment
729
+ const isChatEnv = document.getElementById('chat-messages') !== null;
730
+
731
+ if (isChatEnv) {{
732
+ // Chat environment event listeners
733
+ document.getElementById('send-message-btn').addEventListener('click', () => {{
734
+ this.sendMessage();
735
+ }});
736
+
737
+ // Send message on Enter (but allow Shift+Enter for new lines)
738
+ document.getElementById('message-input').addEventListener('keydown', (e) => {{
739
+ if (e.key === 'Enter' && !e.shiftKey) {{
740
+ e.preventDefault();
741
+ this.sendMessage();
742
+ }}
743
+ }});
744
+ }} else {{
745
+ // Traditional action form submission
746
+ const actionForm = document.getElementById('action-form');
747
+ if (actionForm) {{
748
+ actionForm.addEventListener('submit', (e) => {{
749
+ e.preventDefault();
750
+ this.submitAction();
751
+ }});
752
+ }}
753
+ }}
754
 
755
  // Reset button
756
  document.getElementById('reset-btn').addEventListener('click', () => {{
 
763
  }});
764
  }}
765
 
766
+ async sendMessage() {{
767
+ const messageInput = document.getElementById('message-input');
768
+ const roleSelect = document.getElementById('message-role');
769
+ const message = messageInput.value.trim();
770
+ const role = roleSelect.value;
771
+
772
+ if (!message) {{
773
+ return;
774
+ }}
775
+
776
+ // Add message to chat display immediately
777
+ this.addMessageToChat(role, message);
778
+
779
+ // Clear input
780
+ messageInput.value = '';
781
+
782
+ try {{
783
+ // Send message to server to convert to action and step
784
+ const response = await fetch('/web/step', {{
785
+ method: 'POST',
786
+ headers: {{ 'Content-Type': 'application/json' }},
787
+ body: JSON.stringify({{
788
+ message: {{
789
+ role: role,
790
+ content: message
791
+ }}
792
+ }})
793
+ }});
794
+
795
+ if (!response.ok) {{
796
+ throw new Error(`HTTP error! status: ${{response.status}}`);
797
+ }}
798
+
799
+ const result = await response.json();
800
+ console.log('Message sent:', result);
801
+ }} catch (error) {{
802
+ console.error('Error sending message:', error);
803
+ alert('Error sending message: ' + error.message);
804
+ }}
805
+ }}
806
+
807
+ addMessageToChat(role, content) {{
808
+ const chatMessages = document.getElementById('chat-messages');
809
+ const messageDiv = document.createElement('div');
810
+ messageDiv.className = `chat-message ${{role}}`;
811
+
812
+ messageDiv.innerHTML = `
813
+ <div class="message-role">${{role.charAt(0).toUpperCase() + role.slice(1)}}</div>
814
+ <div class="message-content">${{content}}</div>
815
+ `;
816
+
817
+ chatMessages.appendChild(messageDiv);
818
+ chatMessages.scrollTop = chatMessages.scrollHeight;
819
+ }}
820
+
821
  async submitAction() {{
822
  const formData = new FormData(document.getElementById('action-form'));
823
  const action = {{}};
 
899
  }}
900
 
901
  updateUI(episodeState) {{
902
+ // Check if this is a chat environment
903
+ const isChatEnv = document.getElementById('chat-messages') !== null;
904
+
905
  // Update current state
906
  document.getElementById('env-status').textContent =
907
  episodeState.is_reset ? 'Reset' : 'Running';
 
910
  document.getElementById('step-count').textContent =
911
  episodeState.step_count.toString();
912
 
913
+ if (isChatEnv) {{
914
+ // Update chat interface
915
+ this.updateChatInterface(episodeState);
 
 
 
916
  }} else {{
917
+ // Update traditional observation display
918
+ const observationDiv = document.getElementById('current-observation');
919
+ if (episodeState.current_observation) {{
920
+ observationDiv.textContent = JSON.stringify(
921
+ episodeState.current_observation, null, 2
922
+ );
923
+ }} else {{
924
+ observationDiv.textContent = 'No observation yet';
925
+ }}
926
  }}
927
 
928
  // Update action logs
 
943
  `).join('');
944
  }}
945
  }}
946
+
947
+ updateChatInterface(episodeState) {{
948
+ const chatMessages = document.getElementById('chat-messages');
949
+ if (!chatMessages) return;
950
+
951
+ // Clear existing messages (except system message)
952
+ const systemMessage = chatMessages.querySelector('.chat-message.system');
953
+ chatMessages.innerHTML = '';
954
+ if (systemMessage) {{
955
+ chatMessages.appendChild(systemMessage);
956
+ }}
957
+
958
+ // Add messages from current observation
959
+ if (episodeState.current_observation && episodeState.current_observation.messages) {{
960
+ episodeState.current_observation.messages.forEach(msg => {{
961
+ this.addMessageToChat(msg.role, msg.content);
962
+ }});
963
+ }}
964
+ }}
965
  }}
966
 
967
  // Initialize the web interface when the page loads
 
974
  """.replace('{_generate_action_form_fields(action_fields)}', _generate_action_form_fields(action_fields))
975
 
976
 
977
+ def _generate_action_interface(action_fields: List[Dict[str, Any]], is_chat_env: bool) -> str:
978
+ """Generate either a chat interface or action form based on environment type."""
979
+ if is_chat_env:
980
+ return _generate_chat_interface()
981
+ else:
982
+ return _generate_action_form(action_fields)
983
+
984
+ def _generate_chat_interface() -> str:
985
+ """Generate a chat-style interface for chat environments."""
986
+ return '''
987
+ <!-- Chat Interface -->
988
+ <div class="chat-interface">
989
+ <h3>Chat Interface</h3>
990
+ <div class="chat-messages" id="chat-messages">
991
+ <div class="chat-message system">
992
+ <div class="message-role">System</div>
993
+ <div class="message-content">Chat environment ready. Send a message to start the conversation.</div>
994
+ </div>
995
+ </div>
996
+ <div class="chat-input-container">
997
+ <div class="role-selector">
998
+ <label for="message-role">Role:</label>
999
+ <select id="message-role">
1000
+ <option value="user">User</option>
1001
+ <option value="assistant">Assistant</option>
1002
+ </select>
1003
+ </div>
1004
+ <div class="message-input">
1005
+ <textarea id="message-input" placeholder="Type your message here..." rows="3"></textarea>
1006
+ <button class="btn" id="send-message-btn">Send Message</button>
1007
+ </div>
1008
+ </div>
1009
+ </div>
1010
+ '''
1011
+
1012
+ def _generate_action_form(action_fields: List[Dict[str, Any]]) -> str:
1013
+ """Generate a traditional action form for non-chat environments."""
1014
+ return f'''
1015
+ <!-- Action Form -->
1016
+ <div class="action-form">
1017
+ <h3>Take Action</h3>
1018
+ <form id="action-form">
1019
+ {_generate_action_form_fields(action_fields)}
1020
+ <button type="submit" class="btn" id="step-btn">Step</button>
1021
+ </form>
1022
+ </div>
1023
+ '''
1024
+
1025
  def _generate_action_form_fields(action_fields: List[Dict[str, Any]]) -> str:
1026
  """Generate HTML form fields for action input."""
1027
  if not action_fields: