Skip to content

Conversation

@ryanhoangt
Copy link
Collaborator

@ryanhoangt ryanhoangt commented Sep 18, 2025

Fix #281

@github-actions
Copy link
Contributor

github-actions bot commented Sep 18, 2025

Coverage

Coverage Report
FileStmtsMissCoverMissing
openhands
   __init__.py10100% 
openhands/agent_server
   __init__.py16160%1, 3–5, 9–12, 20–22, 25, 27–29, 31
   __main__.py15150%1, 3, 6–8, 11, 14, 22, 24–27, 29, 38–39
   api.py20200%1–2, 4, 6, 9, 12, 15, 18, 24–28, 31–32, 36–37, 40–42
   config.py42420%1–4, 6, 10–11, 14, 17, 21, 27–28, 31, 34, 38, 41, 47, 54, 61, 67, 73, 77, 79–80, 82, 85–87, 89–90, 93–96, 98, 101, 104, 107, 109–110, 113–114
   conversation_router.py50500%3–4, 6, 8, 11, 18, 21–22, 28–29, 48–50, 55–56, 63–64, 67–68, 70–73, 76–77, 82–84, 90–91, 95–96, 99, 102–106, 109, 112–116, 119–124
   conversation_service.py1951950%1–6, 8, 10–12, 19–22, 25, 28–29, 35–39, 41–48, 50, 57–58, 61–63, 69–70, 72, 75–82, 85–86, 89–93, 96–98, 100–103, 105, 107, 112–113, 115–117, 120–121, 123, 125, 127, 132–136, 140, 144–148, 151–152, 159–160, 173–177, 180, 182–183, 185–191, 193–199, 201–210, 212–215, 217–225, 230–231, 234–236, 238–242, 244, 251–253, 261–263, 265–266, 269–274, 276, 278, 280–281, 283, 285–286, 288, 290–291, 293–294, 297–299, 302, 308–311, 318–319, 323–327, 329, 334, 337, 340, 342–343, 345, 349–351
   event_router.py86860%5–8, 10, 18, 20, 23, 31–32, 35–37, 42–43, 65–70, 73–74, 84–88, 91–92, 94–100, 103–104, 109–113, 119–120, 122–127, 130, 133, 137–141, 147–148, 152–156, 159–168, 170, 173–175, 177–182
   event_service.py1261260%1–4, 6, 13–15, 21–22, 25–26, 32–36, 38–40, 42–45, 47–50, 53, 56, 58, 65–66, 69–71, 73, 78–79, 82–85, 88–89, 92–96, 99–101, 103–106, 108–109, 111–113, 115, 120–121, 123–125, 127, 132–133, 135, 137, 139–143, 145–152, 154–155, 157–158, 160, 162–166, 182–183, 185, 187–190, 192–194, 196, 198–201, 203–207, 209–212, 214–216, 218–220
   middleware.py26260%1, 3–6, 9, 14–15, 23–26, 29–30, 33–34, 37, 46–48, 50, 53–57
   models.py50500%1–4, 6, 8, 12, 19–20, 27, 30, 33–36, 39, 42–43, 46, 52–54, 59–61, 64, 72, 77, 80, 87, 90–93, 96, 99, 102–104, 107–109, 112, 115–116, 119–120, 123–125
   pub_sub.py35350%1–4, 6–7, 10, 13–15, 18, 22–23, 30, 32, 39–42, 44, 51–54, 56, 59, 61, 68–72, 74–75, 78
   utils.py28280%1–4, 6, 9, 11, 14–18, 21, 25–30, 33–36, 39–41, 44, 47
openhands/sdk
   __init__.py16287%27–28
   logger.py732171%33, 57, 64–67, 69–71, 124, 129–131, 134–135, 141–143, 150, 155–156
openhands/sdk/agent
   __init__.py40100% 
   agent.py1954775%64, 71, 78, 82, 99, 117, 124–125, 130–131, 207–208, 210–212, 214–216, 258, 272, 295, 334, 339–341, 344–345, 348, 378–380, 384–386, 396–397, 401–404, 411–412, 416, 420–421, 452, 459
   base.py94792%113, 127, 135–137, 153, 173
   spec.py150100% 
openhands/sdk/context
   __init__.py40100% 
   agent_context.py57296%146, 152
   manager.py330%1, 4–5
   view.py97198%90
openhands/sdk/context/condenser
   __init__.py50100% 
   base.py210100% 
   llm_summarizing_condenser.py39392%44–46
   no_op_condenser.py60100% 
   pipeline_condenser.py13653%45–50
openhands/sdk/context/microagents
   __init__.py40100% 
   exceptions.py50100% 
   microagent.py1432582%130, 133–136, 218–221, 229, 251–252, 257–258, 260, 264, 271–273, 281–283, 337, 339–340
   types.py210100% 
openhands/sdk/context/prompts
   __init__.py20100% 
   prompt.py30583%12, 15, 24, 44–45
openhands/sdk/conversation
   __init__.py80100% 
   conversation.py1291786%95, 110, 138, 146–148, 152–153, 202, 204–205, 207, 225, 307–308, 316–317
   event_store.py101892%50–51, 60, 67, 72–73, 129, 142
   persistence_const.py50100% 
   secrets_manager.py41197%107
   serialization_diff.py00100% 
   state.py101595%141, 164, 200–202
   stuck_detector.py1012080%88, 92–93, 155, 164–165, 168, 193, 196, 202, 209–211, 224, 233, 257–258, 264–265, 271
   types.py60100% 
   visualizer.py94693%90, 147, 169, 186, 218, 220
openhands/sdk/event
   __init__.py70100% 
   base.py74889%55, 75, 87–88, 94, 97–98, 100
   condenser.py28775%37, 39, 41–45
   llm_convertible.py1811691%54, 64–65, 70–71, 251, 285–286, 291, 299, 340–341, 346, 379–380, 385
   metric_events.py130100% 
   types.py70100% 
   user_action.py12191%21
   utils.py120100% 
openhands/sdk/io
   __init__.py40100% 
   base.py14471%7, 11, 15, 19
   local.py561671%43–44, 58, 66–78
   memory.py43490%16, 20, 53–54
openhands/sdk/llm
   __init__.py80100% 
   exceptions.py360100% 
   llm.py3999775%229, 234, 253–254, 286, 349, 355–356, 451, 464–465, 470–471, 473–474, 477–479, 484–486, 490–492, 513–516, 523, 541–542, 570, 576–577, 623, 672, 689–690, 699, 710, 731, 733–738, 740–757, 760–764, 766–767, 773–782, 786–797, 810, 824, 829
   llm_registry.py380100% 
   message.py109496%96, 99, 222–223
   metadata.py150100% 
openhands/sdk/llm/mixins
   fn_call_converter.py3439472%74, 343, 345, 349, 367, 369, 375, 381, 383, 422, 424, 426, 428, 433–434, 481–482, 518–520, 522, 524, 545–547, 553, 575, 601–602, 610–613, 615, 617, 639, 648, 656, 701–704, 708–711, 723, 727, 738, 748, 797–798, 800, 829, 833, 859, 867, 870–871, 876, 905–908, 912–913, 918–919, 924, 973–974, 980, 994, 1006, 1008–1009, 1012–1014, 1016–1017, 1023–1025, 1027–1028, 1030, 1032, 1036, 1038, 1043, 1045–1046, 1049
   non_native_fc.py39392%64, 75, 91
openhands/sdk/llm/utils
   metrics.py111397%17, 117, 311
   model_features.py400100% 
   retry_mixin.py501178%47, 50, 64, 86, 90, 94–95, 105, 110–111, 116
   telemetry.py1361588%71, 94, 99–100, 112–113, 120, 134, 199, 216, 222, 229, 232, 234, 241
   unverified_models.py69494%45–46, 51, 73
   verified_models.py50100% 
openhands/sdk/mcp
   __init__.py50100% 
   client.py26676%48–49, 62–63, 72–73
   definition.py481666%55, 75–80, 82–90
   tool.py401367%36–39, 43, 46, 49–52, 101–102, 107
   utils.py30486%23–24, 27, 30
openhands/sdk/preset
   __init__.py00100% 
   default.py201240%13, 15, 22, 28–29, 31–33, 35–36, 43, 45
openhands/sdk/security
   __init__.py20100% 
   analyzer.py36877%41, 74, 76–77, 79–80, 82, 85
   llm_analyzer.py90100% 
   risk.py12283%21, 33
openhands/sdk/tool
   __init__.py50100% 
   schema.py1151190%23–25, 27, 36, 216–219, 239, 254
   spec.py150100% 
   tool.py921089%66, 107, 167, 170–176
openhands/sdk/tool/builtins
   __init__.py40100% 
   finish.py26196%33
   think.py321359%24, 27–28, 31, 33–37, 39, 51, 57, 74
openhands/sdk/utils
   __init__.py30100% 
   async_executor.py52786%39, 55–56, 84, 88, 102–103
   async_utils.py120100% 
   discriminated_union.py1682286%119–127, 141–142, 236, 329, 356, 399–401, 418, 451, 464, 471, 474
   json.py28280%1–3, 5, 7–8, 11, 14–21, 25, 28, 30–31, 34, 37–38, 40, 43, 45–48
   protocol.py30100% 
   pydantic_diff.py571573%36, 44, 50–58, 60–62, 65
   truncate.py100100% 
   visualize.py17476%14–16, 22
openhands/tools
   __init__.py17382%29–30, 79
openhands/tools/browser_use
   __init__.py330%3, 37, 40
   definition.py1091090%3, 5, 7–10, 14, 17, 20–22, 26–29, 31, 35, 37–38, 40, 46, 49–50, 55, 68, 83, 86–88, 101, 104, 107, 113, 125, 140, 143–145, 158, 161, 164, 167, 179, 194, 197–199, 212, 215, 221, 230, 245, 248–250, 263, 266, 270, 276, 281, 296, 299–301, 314, 317, 323, 332, 347, 350–352, 365, 368, 371, 377, 392, 395–397, 410, 413, 416, 422, 437, 440–442, 455, 458, 464, 472, 487, 490–492, 505, 508, 513, 521, 536, 539–541, 551, 558–561
   impl.py1131130%3–4, 6–8, 12, 15, 18, 25, 28, 33–34, 36, 38, 42, 44, 58–59, 61–70, 73–82, 84–85, 87–91, 93, 95, 97–98, 101, 103–104, 106, 108–109, 112, 114–115, 117, 119–120, 122, 124–125, 127, 129, 131–132, 134–137, 140–141, 144, 146, 148, 151, 153–154, 156, 158–159, 161, 163–164, 167, 169–170, 174, 176–180, 182, 184–189, 191, 193, 195–197, 200, 202, 204–207
   server.py45450%1, 4, 10–11, 13–14, 16–17, 20–21, 24–25, 28, 30–32, 34–35, 38–39, 41, 44, 47–48, 51, 54–55, 57–61, 64–66, 68, 73–78, 80, 87, 89
openhands/tools/execute_bash
   __init__.py40100% 
   constants.py90100% 
   definition.py956135%38, 41, 44–45, 47, 50–52, 54–56, 58, 88, 92–101, 106, 109–111, 114, 116–118, 120, 124–125, 128–130, 132–133, 136–139, 143–145, 150, 154–156, 159–161, 165–166, 168, 245, 247–248, 251, 261
   impl.py402245%52–55, 57–58, 60–62, 64–67, 69, 72, 82–83, 86–89, 91
   metadata.py502354%67–73, 77–78, 83, 85, 87–96, 100–101
openhands/tools/execute_bash/terminal
   __init__.py60100% 
   factory.py492842%24–25, 30, 32, 35, 37–38, 44–46, 74–77, 79–83, 87–89, 91, 97, 107, 111–113
   interface.py691873%43, 52, 62, 71, 76, 85, 94, 99, 104, 112, 145, 157, 162, 171, 180, 185, 191, 193
   subprocess_terminal.py23620313%33–34, 50–53, 56, 59–61, 67–68, 71–74, 76, 79, 81–83, 97–100, 102, 105–106, 109, 112–113, 117–118, 124–126, 128, 131–132, 134, 138–139, 141–142, 144–150, 152–158, 161–166, 168–169, 171–172, 177, 179–187, 191–193, 195–196, 198–199, 202–204, 206–209, 211–212, 214–215, 217–222, 227–229, 231, 234, 237–238, 241–242, 248–250, 252–259, 261–264, 268–276, 289–290, 292, 309–310, 313–314, 316, 318, 320–322, 325–326, 328–330, 332–333, 335–336, 345–346, 350, 352–357, 361–362, 364–367, 369–375, 377–378, 380–384, 388–389, 391–397, 401–402, 405–406, 408–409, 411–413
   terminal_session.py17813524%43, 92, 96–98, 107–108, 119–121, 123–126, 135–136, 140, 144, 147–148, 150, 155, 161, 163–164, 169, 177, 182–185, 198–200, 205, 208–209, 213, 219, 233–235, 240, 243–244, 248, 255, 264, 273–274, 276, 279–283, 285, 288, 290–292, 296–297, 300–302, 306, 311–312, 316–317, 323–325, 336–337, 340–342, 344–346, 349, 359, 363, 366, 369–370, 376–377, 383, 388–389, 392–396, 402, 404–406, 412–416, 419, 422, 425–426, 428–431, 438, 442, 447–448, 455–457, 461, 465, 470–471, 475–476, 479–482, 488–489, 492
   tmux_terminal.py802963%36, 45, 91, 95, 97, 107–108, 110, 118–119, 121, 128, 133, 145–152, 160–161, 163–164, 166, 168–170
openhands/tools/execute_bash/utils
   command.py817211%15–19, 27, 32, 34–35, 37–38, 41–46, 48, 51–53, 55, 58–67, 74–75, 77–79, 81, 83, 90–91, 93, 95–97, 99, 101–102, 105–106, 109, 117, 120–121, 123–124, 127–129, 132–138, 141–145, 150
openhands/tools/str_replace_editor
   __init__.py30100% 
   definition.py621280%84, 96, 116, 119, 122, 129, 131, 133, 135, 207, 210, 213
   editor.py22817025%74, 76–77, 80, 98, 101, 111–115, 121–129, 131, 149–150, 170–171, 175, 179–180, 189, 193–196, 204–205, 209–211, 217, 220, 225, 228, 231–232, 235, 238–239, 243, 247, 262–264, 273, 276, 282, 287, 289–293, 295, 297, 301–302, 306–307, 316–317, 319–322, 324, 331–332, 338–340, 348–352, 356, 358–359, 366, 369, 374–375, 377, 401–402, 424–425, 427–428, 434, 437, 441–447, 450–451, 454–459, 462, 465–466, 470, 473–474, 477, 479–480, 486, 490, 509, 514–517, 519, 527, 534, 541, 552–555, 557, 559, 586–589, 598–599, 628–630, 632–641, 646–649, 662–663, 668, 673, 679, 685
   exceptions.py221340%5–6, 9, 16–18, 25–27, 38–41
   impl.py261157%31–32, 51–52, 54–56, 65–68
openhands/tools/str_replace_editor/utils
   __init__.py00100% 
   config.py20100% 
   constants.py50100% 
   diff.py641576%24, 40–42, 45–47, 50, 90–93, 107–108, 115
   encoding.py542750%42–43, 46–48, 51, 54, 60, 64–65, 67, 69, 78, 80–81, 84, 87–90, 93, 96–97, 114, 128, 130–131
   file_cache.py954849%43–46, 49–50, 54, 59, 61, 64, 69, 72–73, 87, 95–98, 108–112, 115–120, 126–130, 133–135, 138–140, 143–148, 151, 154
   history.py663645%58–60, 66–68, 70–71, 74–76, 78–79, 82, 85–86, 88, 92–94, 98–99, 102–104, 107, 111–113, 115–120, 122
   shell.py231630%30, 32–34, 38, 40, 51–55, 62–63, 70–72
openhands/tools/task_tracker
   __init__.py220%1, 10
   definition.py1321320%1–4, 6–7, 9–11, 20, 23–26, 33, 36, 40, 45–46, 48, 51–53, 55–56, 59–60, 62, 65, 68, 71–72, 76–78, 80–81, 83, 85, 87–88, 91, 94–96, 98–99, 102–108, 110–112, 115, 117–120, 122, 125, 128–129, 131–132, 134–135, 137, 140, 143, 150–151, 154–155, 157, 159, 161, 163–165, 171, 173–174, 179–180, 184, 191, 193–194, 196–198, 202–203, 205–208, 210, 212, 214–215, 217–219, 221–225, 229, 231, 233–234, 236–237, 239, 241–245, 249, 382, 396, 399–400, 407, 410
openhands/tools/utils
   __init__.py00100% 
TOTAL6807267360% 

@ryanhoangt ryanhoangt marked this pull request as ready for review September 19, 2025 11:42
Copy link
Collaborator

@xingyaoww xingyaoww left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly LGTM - a few things we need to address

if is_stuck:
logger.warning("Stuck pattern detected.")
# FIXME: raise error or handle differently?
break
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should add a new AgentStatus to Agent stuck in loop maybe?

Copy link
Collaborator Author

@ryanhoangt ryanhoangt Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, I added a new status in the latest commit.

BTW, WDYT about the idea that, instead of raising an error and stop, we can (1) send a MessageAction to prompt the agent to do differently? Or even better but more complex, we can try to switch to a different model to handle this current turn (given that strong LLMs rarely get stuck I believe) -- If that makes sense I can create an issue for it.

@openhands-ai
Copy link

openhands-ai bot commented Sep 22, 2025

Looks like there are a few issues preventing this PR from being merged!

  • GitHub Actions are failing:
    • Run tests

If you'd like me to help, just leave a comment, like

@OpenHands please fix the failing actions on PR #335 at branch `ht/stuck-detector`

Feel free to include any additional details that might help me get this PR into a better state.

You can manage your notification settings

Copy link
Collaborator

@xingyaoww xingyaoww left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@xingyaoww xingyaoww enabled auto-merge (squash) September 22, 2025 17:35
@github-actions
Copy link
Contributor

Agent Server image for this PR

Pull (multi-arch manifest):

docker pull ghcr.io/all-hands-ai/agent-server:83a04d5

Run:

docker run -it --rm \
  -p 8000:8000 \
  --name agent-server-83a04d5 \
  ghcr.io/all-hands-ai/agent-server:83a04d5

This tag is a multi-arch manifest (amd64/arm64). Your client pulls the right arch automatically.

@xingyaoww xingyaoww merged commit e37d9cd into main Sep 22, 2025
15 checks passed
@xingyaoww xingyaoww deleted the ht/stuck-detector branch September 22, 2025 17:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Port over stuck-in-loop detector

3 participants