Skip to content
This repository was archived by the owner on Jul 19, 2026. It is now read-only.

feat: add you.com search provider - #3

Merged
haasonsaas merged 2 commits into
evalops:mainfrom
mouse-value-add:feat/youcom-search-provider
Apr 12, 2026
Merged

feat: add you.com search provider#3
haasonsaas merged 2 commits into
evalops:mainfrom
mouse-value-add:feat/youcom-search-provider

Conversation

@mouse-value-add

Copy link
Copy Markdown
Contributor

Hi maintainers — thanks for building and maintaining Agent Harness. The unified tool registry + provider hot-swapping design makes this a strong fit for lightweight web-search integrations that work consistently across OpenAI and Claude.

Why this repo

I selected agent-harness because it is an active, focused agent runtime with a shared tool abstraction and no built-in open web-search provider at the harness layer. That makes it a good place to add an optional provider adapter without changing core execution behavior.

What changed

  • Added register_you_com_search_tool(...) in agent_harness.py
    • Registers a you_search(query: str, num_results: int = 5) tool in the existing global ToolRegistry
    • Calls You.com Search API (https://api.ydc-index.io/search) with X-API-Key
    • Formats top results into compact, agent-friendly text output
  • Added fallback/error handling:
    • Missing API key returns a non-fatal setup message
    • HTTP/network/JSON errors return concise failure text (no crash)
    • Empty result sets return No web results found.
  • Added tests in tests/test_you_search_tool.py:
    • Missing API key behavior
    • Successful response formatting (mocked API response)
  • Updated README with:
    • setup via YOUCOM_API_KEY
    • usage example
    • fallback/error behavior notes
    • API reference entry

Setup

export YOUCOM_API_KEY="<your-key>"
from agent_harness import register_you_com_search_tool

register_you_com_search_tool(name="you_search")
# then include "you_search" in HarnessConfig.tool_names

Validation

  • Ran full test suite: python3 -m pytest -q
  • Result: all tests passed (including new coverage for the You.com adapter)

Why this helps agent intelligence

This gives agents a straightforward way to retrieve fresh web context through the existing tool registry, while preserving the current provider-agnostic architecture. It enables web-aware reasoning without introducing provider lock-in or breaking existing tool flows.

Backward compatibility: existing APIs and behaviors remain unchanged unless developers explicitly register/use the new adapter.

If you'd prefer different naming, output format, or endpoint/config options, I’m happy to adjust to match your maintainer preferences.

@cursor

cursor Bot commented Apr 11, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Adds a new network-calling tool that introduces external dependency and variable API response shapes; failures are handled as non-fatal strings but output formatting and error paths need review.

Overview
Adds an optional You.com web-search adapter via register_you_com_search_tool(), registering a you_search(query, num_results) tool that calls the You.com Search API and formats top results into compact text.

Includes non-fatal fallback behavior for missing API keys, empty queries/results, and HTTP/network/JSON errors, plus README usage/docs updates and new tests that mock urllib.request.urlopen to validate configuration and output formatting.

Reviewed by Cursor Bugbot for commit 98c655d. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread agent_harness.py
Comment thread agent_harness.py
@haasonsaas
haasonsaas merged commit 87fa83d into evalops:main Apr 12, 2026
3 checks passed

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 98c655d. Configure here.

Comment thread agent_harness.py
line += f"\n {link}"
if snippet:
line += f"\n {snippet[:220]}"
lines.append(line)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Response parsing outside try-except can raise unhandled exceptions

Medium Severity

The try/except block only covers the HTTP request and json.loads call (lines 309–313), but all subsequent response parsing (lines 315–343) is unprotected. If json.loads returns a non-dict value (e.g., None or a string from an unusual API response), payload.get("results") raises an AttributeError. Likewise, if individual items in the hits list are not dicts, hit.get("title") crashes the same way. The PR claims "no crash" error handling, but this gap allows unhandled exceptions to propagate to the caller.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 98c655d. Configure here.

Comment thread agent_harness.py
return "No web results found."

lines = []
for idx, hit in enumerate(hits[: max(1, min(num_results, 10))], start=1):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

API receives uncapped num_results but display silently truncates

Low Severity

The num_results value is sent to the API as num_web_results without any cap (line 298), but results are locally truncated to max(1, min(num_results, 10)) (line 329). When a caller requests, say, 20 results, the API fetches and returns all 20 but only 10 are displayed. The cap applied to the display loop isn't applied to the API request parameter, causing wasted bandwidth and silently incomplete output that could mislead the LLM agent.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 98c655d. Configure here.

Comment thread agent_harness.py
if isinstance(snippets, list):
snippet = " ".join(str(item) for item in snippets if item)
else:
snippet = hit.get("snippet") or hit.get("description") or ""

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Empty snippets list prevents fallback to description field

Low Severity

When a result item has "snippets": [] (an empty list), isinstance(snippets, list) is True, so the code enters the list-join branch and produces an empty string. It never falls back to hit.get("description") in the else branch, even though a useful description may be available. The result is output with only a title and URL but no descriptive text, despite the API providing one. The empty-list case needs to fall through to the description fallback.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 98c655d. Configure here.

@mouse-value-add

Copy link
Copy Markdown
Contributor Author

Follow-up: the merged implementation in #3 used a legacy You Search endpoint shape. I opened #4 to align with the latest docs (, , and with backward-compatible alias). Please review/merge #4 to complete the fix.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants