Skip to content
Open
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
48 changes: 44 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ benefit from Linkup services to the full extent. 📝

- ✅ **Simple and intuitive API client.**
- 🔍 **Support all Linkup entrypoints and parameters.**
- ⚡ **Supports synchronous and asynchronous requests.**
- 🔒 **Handles authentication and request management.**
- ⚡ **Support synchronous and asynchronous calls.**
- 🔒 **Handle authentication and request management.**

## 📦 Installation

Expand Down Expand Up @@ -90,9 +90,10 @@ The `search` function also supports three output types:
user-defined schema

```python
from linkup import LinkupClient, LinkupSourcedAnswer
from typing import Any

from linkup import LinkupClient, LinkupSourcedAnswer

client = LinkupClient() # API key can be read from the environment variable or passed as an argument
search_response: Any = client.search(
query="What are the 3 major events in the life of Abraham Lincoln?",
Expand Down Expand Up @@ -149,7 +150,46 @@ Check the code or the
[official documentation](https://docs.linkup.so/pages/documentation/api-reference/endpoint/post-fetch)
for the detailed list of available parameters.

#### ⌛ Asynchronous Calls

All the Linkup main functions come with an asynchronous counterpart, with the same behavior and the
same name prefixed by `async_` (e.g. `async_search` for `search`). This should be favored in
production use cases to avoid blocking the main thread while waiting for the Linkup API to respond.
This makes possible to call the Linkup API several times concurrently for instance.

```python
import asyncio
from typing import Any

from linkup import LinkupClient, LinkupSourcedAnswer

async def main() -> None:
client = LinkupClient() # API key can be read from the environment variable or passed as an argument
search_response: Any = await client.async_search(
query="What are the 3 major events in the life of Abraham Lincoln?",
depth="deep", # "standard" or "deep"
output_type="sourcedAnswer", # "searchResults" or "sourcedAnswer" or "structured"
structured_output_schema=None, # must be filled if output_type is "structured"
)
assert isinstance(search_response, LinkupSourcedAnswer)
print(search_response.model_dump())

asyncio.run(main())
# Response:

Choose a reason for hiding this comment

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

Maybe wrap the response in something more readable? We have many beautiful options in the markdown

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you have in mind more precisely?
The reason I use this format is to reflect clearly the structure of the output, and to keep a dependency-free snippet (except for the sdk itself)

# {
# answer="The three major events in the life of Abraham Lincoln are: 1. ...",
# sources=[
# {
# "name": "HISTORY",
# "url": "https://www.history.com/topics/us-presidents/abraham-lincoln",
# "snippet": "Abraham Lincoln - Facts & Summary - HISTORY ..."
# },
# ...
# ]
# }
```

#### 📚 More Examples

See the `examples/` directory for more examples and documentation, for instance on how to use Linkup
entrypoints using asynchronous functions.
entrypoints using asynchronous functions to call the Linkup API several times concurrenly.
8 changes: 6 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,14 @@ line-length = 100
target-version = "py39"

[tool.ruff.lint]
select = ["E", "F", "I", "S", "UP"]
extend-ignore = ["D107"]
pydocstyle = { convention = "google" }
select = ["D", "E", "F", "I", "S", "UP"]

[tool.ruff.lint.extend-per-file-ignores]
"tests/**/*_test.py" = ["S101"]
"examples/*.py" = ["D"]
"src/linkup/__init__.py" = ["D104"]
"tests/**/*test.py" = ["D", "S101"]

[build-system]
build-backend = "hatchling.build"
Expand Down
2 changes: 2 additions & 0 deletions src/linkup/client.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Linkup client, the entrypoint for Linkup functions."""

import json
import os
from datetime import date
Expand Down
3 changes: 3 additions & 0 deletions src/linkup/errors.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
"""Linkup custom errors."""


class LinkupInvalidRequestError(Exception):
"""Invalid request error, raised when the Linkup API returns a 400 status code.

Expand Down
2 changes: 2 additions & 0 deletions src/linkup/types.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Input and output types for Linkup functions."""

from typing import Any, Literal, Optional, Union

from pydantic import BaseModel, ConfigDict, Field
Expand Down