From 64ac4409973cfc262d9c5a87640f1c42e517e99d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 5 Aug 2026 08:05:32 +0000 Subject: [PATCH] feat: expose fetch favicon Co-authored-by: william --- README.md | 3 ++- src/linkup/_types.py | 2 ++ tests/unit/client_test.py | 44 +++++++++++++++++++++++++++++++++------ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 12c91e0..79a6f35 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ for the detailed list of available parameters. #### 🪝 Fetch The `fetch` function can be used to retrieve the content of a given web page in a cleaned up -markdown format. +markdown format, together with the website's favicon URL. You can use the `render_js` flag to execute the JavaScript code of the page before returning the content, and set `include_raw_content` to include the raw page content and its content type. @@ -157,6 +157,7 @@ Which prints: ```bash { markdown="Get started for free, no credit card required...", + favicon="https://favicons.linkup.so?domain=docs.linkup.so", raw_content="......", content_type="html" } diff --git a/src/linkup/_types.py b/src/linkup/_types.py index 5c587e3..68c5db1 100644 --- a/src/linkup/_types.py +++ b/src/linkup/_types.py @@ -114,6 +114,7 @@ class LinkupFetchResponse(_LinkupBaseModel): Attributes: markdown: The cleaned up markdown content. + favicon: The URL of the website favicon. raw_content: The optional raw page content. content_type: The type of the raw page content, if returned. raw_html: The optional raw HTML content. Deprecated; use raw_content instead. @@ -121,6 +122,7 @@ class LinkupFetchResponse(_LinkupBaseModel): """ markdown: str + favicon: str raw_content: str | None = pydantic.Field(default=None, validation_alias="rawContent") content_type: str | None = pydantic.Field(default=None, validation_alias="contentType") raw_html: str | None = pydantic.Field(default=None, validation_alias="rawHtml") diff --git a/tests/unit/client_test.py b/tests/unit/client_test.py index 5c78bb1..ec51f3e 100644 --- a/tests/unit/client_test.py +++ b/tests/unit/client_test.py @@ -910,8 +910,17 @@ def test_research_with_iso_datetime_string_dates( ( {"url": "https://example.com"}, {"url": "https://example.com"}, - b'{"markdown": "Some web page content"}', - linkup.FetchResponse(markdown="Some web page content", raw_html=None), + b""" + { + "favicon": "https://favicons.linkup.so?domain=example.com", + "markdown": "Some web page content" + } + """, + linkup.FetchResponse( + favicon="https://favicons.linkup.so?domain=example.com", + markdown="Some web page content", + raw_html=None, + ), ), ( { @@ -924,12 +933,14 @@ def test_research_with_iso_datetime_string_dates( }, b""" { + "favicon": "https://favicons.linkup.so?domain=example.com", "markdown": "# Some web page content", "rawContent": "...", "contentType": "html" } """, linkup.FetchResponse( + favicon="https://favicons.linkup.so?domain=example.com", markdown="# Some web page content", raw_content="...", content_type="html", @@ -948,14 +959,33 @@ def test_research_with_iso_datetime_string_dates( "renderJs": True, "extractImages": True, }, - b'{"markdown": "#Some web page content", "rawHtml": "..."}', - linkup.FetchResponse(markdown="#Some web page content", raw_html="..."), + b""" + { + "favicon": "https://favicons.linkup.so?domain=example.com", + "markdown": "#Some web page content", + "rawHtml": "..." + } + """, + linkup.FetchResponse( + favicon="https://favicons.linkup.so?domain=example.com", + markdown="#Some web page content", + raw_html="...", + ), ), ( {"url": "https://example.com", "timeout": 15.0}, {"url": "https://example.com"}, - b'{"markdown": "Some web page content"}', - linkup.FetchResponse(markdown="Some web page content", raw_html=None), + b""" + { + "favicon": "https://favicons.linkup.so?domain=example.com", + "markdown": "Some web page content" + } + """, + linkup.FetchResponse( + favicon="https://favicons.linkup.so?domain=example.com", + markdown="Some web page content", + raw_html=None, + ), ), ] @@ -1218,6 +1248,7 @@ def test_create_tasks(mocker: MockerFixture, client: linkup.Client) -> None: }, "output": { "contentType": "html", + "favicon": "https://favicons.linkup.so?domain=example.com", "images": [ { "alt": "hero", @@ -1281,6 +1312,7 @@ def test_create_tasks(mocker: MockerFixture, client: linkup.Client) -> None: assert tasks_response[0].input.structured_output_schema == {"type": "object"} assert isinstance(tasks_response[1], linkup.FetchTask) assert tasks_response[1].output is not None + assert tasks_response[1].output.favicon == "https://favicons.linkup.so?domain=example.com" assert tasks_response[1].output.images is not None assert tasks_response[1].output.images[0].url == "https://example.com/image.png" assert tasks_response[1].output.raw_content == "Fetched content"