Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,12 +230,27 @@ Every constructor accepts:
| `timeout` | Timeout passed to the SerpApi SDK client |
| `name` | Tool name presented to the model |
| `mode` | Result detail level; compact mode is the default, while full mode keeps supporting sections and all fields on retained results |
| `response_format` | Output serialization; Markdown is the default, while `SearchResultFormat.JSON` returns compact or full JSON text |
| `result_limit` | Maximum items kept in each result list in either mode; defaults vary by tool; use `None` for all results |

`web_search` and `shopping_search` also accept `allowed_engines` and `default_engine`. The tool offers only the engine values you configure.

Use `default_params` for documented SerpApi settings that should stay under your application's control, such as locale, currency, safe search, or pagination. Use `result_limit` to control how many results the tool returns. The agent continues to supply only the inputs described by its search tool.

Tools return Markdown by default. Markdown keeps links and tables readable without JSON syntax overhead. Application code that needs structured fields can opt into JSON:

```python
import json

from serpapi_search_tools import SearchResultFormat, web_search

search = web_search(
provider="function",
response_format=SearchResultFormat.JSON,
)
result = json.loads(search(query="Python packaging"))
```

```python
tool = news_search(
default_params={"hl": "en", "gl": "us"},
Expand Down
26 changes: 13 additions & 13 deletions assets/docs-mobile.css
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
/* Mobile corrections layered over Great Docs 0.14. */
@media (max-width: 991.98px) {
body.nav-sidebar:not(.gd-project-home) #quarto-header > .navbar {
display: none;
}

body.nav-sidebar:not(.gd-project-home) #quarto-header,
body.nav-sidebar:not(.gd-project-home) .quarto-secondary-nav {
transform: none !important;
Expand All @@ -17,47 +13,51 @@
display: none !important;
}

body.gd-project-home .navbar-container {
body.nav-sidebar .navbar-container {
flex-wrap: nowrap;
}

body.gd-project-home .navbar-toggler {
body.nav-sidebar .navbar-toggler {
order: 1;
flex: 0 0 auto;
}

body.gd-project-home .navbar-brand-container {
body.nav-sidebar .navbar-brand-container {
order: 2;
flex: 1 1 auto !important;
width: auto !important;
min-width: 0;
max-width: none;
max-width: none !important;
margin-left: 0 !important;
margin-right: auto !important;
overflow: hidden;
}

body.gd-project-home .navbar-brand {
body.nav-sidebar .navbar-brand {
min-width: 0;
max-width: 100%;
margin-right: 0;
}

body.gd-project-home .navbar-title {
body.nav-sidebar .navbar-title {
display: block;
font-size: 1rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

body.gd-project-home .quarto-navbar-tools {
body.nav-sidebar .quarto-navbar-tools {
order: 3;
flex: 0 0 auto;
padding: 0;
}

body.gd-project-home #gd-navbar-widgets {
body.nav-sidebar #gd-navbar-widgets {
width: auto;
}

body.gd-project-home #github-widget,
body.nav-sidebar #github-widget,
body.gd-project-home .gd-meta-sidebar {
display: none;
}
Expand Down
18 changes: 12 additions & 6 deletions assets/docs-mobile.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,25 @@

function isProjectHomepage() {
var path = window.location.pathname;
if (path.endsWith("/")) return true;
if (!path.endsWith("/index.html")) return false;
return !/(?:\/docs\/cookbook|\/user-guide|\/docs\/sdk-examples|\/reference)\/index\.html$/.test(
path,
);
if (
/(?:\/docs\/cookbook|\/user-guide|\/docs\/sdk-examples|\/reference)(?:\/|$)/.test(
path,
)
) {
return false;
}
return path.endsWith("/") || path.endsWith("/index.html");
}

function currentArea() {
var path = window.location.pathname;
if (path.indexOf("/docs/cookbook/") !== -1) return "cookbook";
if (path.indexOf("/docs/sdk-examples/") !== -1) return "examples";
if (path.indexOf("/reference/") !== -1) return "reference";
return "guide";
if (isProjectHomepage() || path.indexOf("/user-guide/") !== -1) {
return "guide";
}
return null;
}

function addUserGuideNavbarLink() {
Expand Down
34 changes: 34 additions & 0 deletions assets/serpapi-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions cookbook/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Choose the SDK you use and run a complete agent with live SerpApi search.
Every entry includes setup instructions, an editable prompt, a runnable agent,
and a Markdown report you can inspect.

Every cookbook agent receives compact Markdown from its SerpApi tools. The scripts rely on the package default and do not parse structured response fields.

| SDK | Agent | SerpApi capabilities |
| --- | --- | --- |
| [LangChain](langchain/) | Deep market research brief | Web and news |
Expand Down
8 changes: 2 additions & 6 deletions cookbook/semantic-kernel/README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
# Semantic Kernel plan-and-execute competitor brief

This ChatCompletionAgent follows an explicit plan, resolves the plan with
search plugins, and writes a competitor brief to
`cookbook-output/semantic-kernel-competitor-brief.md`.
The kernel follows an explicit plan, resolves it with automatic calls to search plugins, and writes a competitor brief to `cookbook-output/semantic-kernel-competitor-brief.md`.

## Original source

Inspired by Semantic Kernel's official Python
[plan-and-execute process](https://github.com/microsoft/semantic-kernel/blob/main/python/samples/concepts/processes/plan_and_execute.py).

This adaptation was **enhanced with SerpApi** by replacing the sample's
provider-native web search with typed web and news plugins and by applying the
workflow to a concrete competitor-analysis artifact.
This adaptation replaces the sample's provider-native web search with typed SerpApi web and news plugins. It applies the workflow to a concrete competitor-analysis artifact.

## Run

Expand Down
40 changes: 22 additions & 18 deletions cookbook/semantic-kernel/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@

from dotenv import load_dotenv
from semantic_kernel import Kernel
from semantic_kernel.agents import ChatCompletionAgent
from semantic_kernel.connectors.ai import FunctionChoiceBehavior
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.connectors.ai.open_ai import (
OpenAIChatCompletion,
OpenAIChatPromptExecutionSettings,
)
from semantic_kernel.functions import KernelArguments

from serpapi_search_tools import news_search, web_search

Expand Down Expand Up @@ -50,7 +53,13 @@ def _write_report(text: str) -> Path:
async def main() -> None:
_require_serpapi_key()
kernel = Kernel()
plugin = kernel.add_functions(
kernel.add_service(
OpenAIChatCompletion(
ai_model_id=MODEL,
api_key=_require_env("OPENAI_API_KEY"),
)
)
kernel.add_functions(
"serpapi",
[
web_search(
Expand All @@ -65,26 +74,21 @@ async def main() -> None:
),
],
)
agent = ChatCompletionAgent(
name="competitor_research_agent",
instructions=(
settings = OpenAIChatPromptExecutionSettings(
function_choice_behavior=FunctionChoiceBehavior.Auto(),
)
response = await kernel.invoke_prompt(
(
"Follow a plan-and-execute process: state a short plan, search each open "
"question, inspect evidence quality, and then write the final competitor brief. "
"Do not invent company facts or omit source URLs."
"Do not invent company facts or omit source URLs.\n\n"
f"{PROMPT}"
),
service=OpenAIChatCompletion(
ai_model_id=MODEL,
api_key=_require_env("OPENAI_API_KEY"),
),
plugins=[plugin],
function_choice_behavior=FunctionChoiceBehavior.Auto(),
arguments=KernelArguments(settings=settings),
)
responses: list[str] = []
async for response in agent.invoke(messages=PROMPT):
responses.append(str(response))
if not responses:
if response is None:
raise RuntimeError("Semantic Kernel completed without a final report.")
report = responses[-1]
report = str(response)
path = _write_report(report)
print(report)
print(f"\nSaved report to {path}")
Expand Down
2 changes: 2 additions & 0 deletions docs/cookbook/index.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Start with the SDK you already use. Every recipe turns an official framework
pattern into a complete, source-backed workflow and saves a Markdown artifact
you can inspect, edit, or share.

The cookbook uses compact Markdown responses from each SerpApi tool. The agents pass search results directly to models and do not parse JSON fields.

## Choose a cookbook

| SDK | Outcome | Search capabilities |
Expand Down
27 changes: 12 additions & 15 deletions docs/cookbook/semantic-kernel.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ description: Apply a plan-and-execute loop to live competitor research.

## What you'll build

A Semantic Kernel `ChatCompletionAgent` that states a research plan, resolves
each open question with search plugins, checks the evidence quality, and writes
a competitor brief with visible gaps.
A Semantic Kernel prompt that states a research plan, resolves each open question with automatic calls to search plugins, checks the evidence quality, and writes a competitor brief with visible gaps.

**Default brief:** Compare three US residential solar-financing platforms using
company and product facts plus recent developments. Include source URLs and
Expand All @@ -30,13 +28,14 @@ unresolved evidence gaps.

## Core agent setup

This excerpt shows how the SerpApi functions become one Semantic Kernel
plugin. The complete script also configures the chat service, consumes the
agent response stream, and saves the competitor brief.
This excerpt shows how the SerpApi functions become one Semantic Kernel plugin. The complete script also configures the chat service, invokes the prompt, and saves the competitor brief.

```python
kernel = Kernel()
plugin = kernel.add_functions(
kernel.add_service(
OpenAIChatCompletion(ai_model_id=MODEL, api_key=api_key),
)
kernel.add_functions(
"serpapi",
[
web_search(
Expand All @@ -46,13 +45,13 @@ plugin = kernel.add_functions(
news_search(provider="semantic-kernel"),
],
)
agent = ChatCompletionAgent(
name="competitor_research_agent",
instructions="Plan, search each open question, and inspect evidence quality.",
service=OpenAIChatCompletion(ai_model_id=MODEL, api_key=api_key),
plugins=[plugin],
settings = OpenAIChatPromptExecutionSettings(
function_choice_behavior=FunctionChoiceBehavior.Auto(),
)
response = await kernel.invoke_prompt(
prompt,
arguments=KernelArguments(settings=settings),
)
```

[View the complete `main.py` →](https://github.com/serpapi/serpapi-search-tools-python/blob/main/cookbook/semantic-kernel/main.py)
Expand All @@ -70,9 +69,7 @@ uv run --isolated --no-project --with 'serpapi-search-tools[semantic-kernel]' --
Optional controls: `OPENAI_MODEL`, `COOKBOOK_PROMPT`, and
`COOKBOOK_OUTPUT_DIR`.

**Native plugin shape:** The two search functions are registered with the
Kernel as one plugin, while automatic function choice lets the agent select
the right evidence surface.
**Native plugin shape:** The two search functions are registered with the Kernel as one plugin. Automatic function choice lets the kernel select the right evidence surface.

## Inspect the result

Expand Down
4 changes: 2 additions & 2 deletions docs/sdk_examples/openai_agents.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ async def main():
asyncio.run(main())
```

The agent decides when to call the tool, receives the SerpApi JSON, and writes a
final answer.
The agent decides when to call the tool, receives compact SerpApi Markdown, and
writes a final answer.

## Try it

Expand Down
8 changes: 3 additions & 5 deletions docs/user_guide/01-introduction.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,14 @@ SDKs are installed, pass `provider=` to choose one explicitly.
For scripts, tests, or a framework that is not listed, request a callable:

```python
import json

from serpapi_search_tools import web_search

search = web_search(provider="function")
response = json.loads(search(query="Python packaging"))
print(response.get("organic_results", []))
response = search(query="Python packaging")
print(response)
```

Every tool returns compact JSON text by default, including its main result sections and useful web answer sections. `result_limit` controls how many items are kept in each result list in compact and full modes. Set `mode=SearchResultMode.FULL` and `result_limit=None` when application code needs every response section, field, and returned result.
Every tool returns compact Markdown by default, including its main result sections and useful web answer sections. `result_limit` controls how many rows are kept in each result table in compact and full modes. Application code that needs structured fields can opt into `SearchResultFormat.JSON`. Set `mode=SearchResultMode.FULL` and `result_limit=None` when application code needs every response section, field, and returned result.

## Next steps

Expand Down
4 changes: 2 additions & 2 deletions docs/user_guide/02-quickstart.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ python search_agent.py
OpenAI `FunctionTool`.
2. The agent decided whether it needed the tool and supplied a text query plus
a supported web engine.
3. The package validated the arguments, called SerpApi, and returned the
structured search response to the agent as JSON text.
3. The package validated the arguments, called SerpApi, and returned compact
Markdown search results to the agent.
4. The model used those results to write the final response.

The default web engine is `google_light`, which is a good fast starting point
Expand Down
Loading
Loading