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
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ repos:
hooks:
- id: ruff-format
name: ruff format
entry: uvx ruff format --force-exclude --check
entry: uv run ruff format --force-exclude --check
language: system
types: [python]
- id: ruff-check
name: ruff check
entry: uvx ruff check --force-exclude --fix
entry: uv run ruff check --force-exclude --fix
language: system
types: [python]
- id: ty-check
name: ty type check
entry: uvx ty check .
entry: uv run ty check .
language: system
types: [python]
pass_filenames: false
7 changes: 5 additions & 2 deletions docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Tip:
## Type Check

```bash
uvx ty check .
uv run ty check .
```

## Code Quality Checks
Expand All @@ -49,9 +49,12 @@ uv run ruff check .
uv run ruff format --check .

# Type check
uvx ty check .
uv run ty check .
```

> **Linter versions are pinned.** `ruff` and `ty` are pinned to exact versions in the
> `pyproject.toml` `[dependency-groups] dev` section (and locked in `uv.lock`) for consistent runs across local and CI.

## Pre-commit Hooks (Recommended)

Automatically run quality checks before each commit using pre-commit hooks.
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ dev = [
"pytest-asyncio>=1.0.0",
"pytest-bdd>=7.2.0",
"python-dotenv>=1.0.0",
"ty>=0.0.21",
"ty==0.0.64",
"cryptography>=46.0.3",
"ruff>=0.8.0",
"ruff==0.16.0",
"starlette>=0.40.0",
"anyio>=3.6.2",
"httpx>=0.27.0",
Expand Down
13 changes: 12 additions & 1 deletion src/sap_cloud_sdk/adms/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ relation = client.relations.create(

# Upload bytes to the presigned URL (outside SDK)
import requests

upload_url = relation.document.document_content_upload_urls[0]
requests.put(upload_url, data=open("Invoice.pdf", "rb"))
```
Expand Down Expand Up @@ -82,6 +83,7 @@ client = create_client(user_jwt=request.headers["Authorization"].split()[1])
```python
from sap_cloud_sdk.adms import create_client, TokenCache


# By default tokens are cached in-process via InMemoryTokenCache.
# For multi-instance deployments (Kyma replicas > 1, CF instances > 1),
# implement your own TokenCache subclass backed by the shared cache your
Expand All @@ -91,13 +93,21 @@ class MySharedCache(TokenCache):
def set(self, key, token, ttl_seconds): ...
def delete(self, key): ...


client = create_client(token_cache=MySharedCache())
```

## Async Client

```python
from sap_cloud_sdk.adms import create_async_client, BaseType, CreateDocumentInput, CreateDocumentRelationInput, RelationQueryOptions
from sap_cloud_sdk.adms import (
create_async_client,
BaseType,
CreateDocumentInput,
CreateDocumentRelationInput,
RelationQueryOptions,
)


async def main():
async with create_async_client() as client:
Expand Down Expand Up @@ -203,6 +213,7 @@ job = client.jobs.start_zip_download(params)

# Poll until terminal state
import time

while not job.job_status or not job.job_status.is_terminal():
time.sleep(2)
job = client.jobs.get_status(job.job_id)
Expand Down
6 changes: 6 additions & 0 deletions src/sap_cloud_sdk/agent_decorators/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ from sap_cloud_sdk.agent_decorators import (
```python
from sap_cloud_sdk.agent_decorators import prompt_section, agent_model


# Define a prompt with a coded default
@prompt_section(
key="prompts.system",
Expand All @@ -30,6 +31,7 @@ from sap_cloud_sdk.agent_decorators import prompt_section, agent_model
def system_prompt() -> str:
return "You are a helpful assistant."


# Define the model selection
@agent_model(key="config.model", label="LLM Model")
def model_name() -> str:
Expand All @@ -45,6 +47,7 @@ Expose a prompt section for editing.
```python
from sap_cloud_sdk.agent_decorators import prompt_section


@prompt_section(
key="prompts.identity",
label="Agent Identity",
Expand All @@ -62,6 +65,7 @@ Expose a configuration value for editing.
```python
from sap_cloud_sdk.agent_decorators import agent_config


@agent_config(
key="config.temperature",
label="Temperature",
Expand All @@ -78,6 +82,7 @@ Expose a model selection. The `description` parameter is optional.
```python
from sap_cloud_sdk.agent_decorators import agent_model


@agent_model(
key="config.model",
label="Default Model",
Expand All @@ -93,6 +98,7 @@ def default_model() -> str:
from sap_cloud_sdk.agent_decorators.exceptions import AgentDecoratorError

try:

@prompt_section(key="", label="L", description="D")
def bad():
return ""
Expand Down
8 changes: 5 additions & 3 deletions src/sap_cloud_sdk/agent_memory/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,7 @@ elif total <= PAGE_SIZE:
agent_id="my-agent", invoker_id="user-123", limit=total
)
else:

def iter_all_memories(client, agent_id, invoker_id, page_size=PAGE_SIZE):
offset = 0
while True:
Expand Down Expand Up @@ -780,7 +781,8 @@ The default `threshold` of `0.6` may be too strict for your data. Try a lower va

```python
results = client.search_memories(
agent_id="my-agent", invoker_id="user-123",
agent_id="my-agent",
invoker_id="user-123",
query="user display preferences",
threshold=0.3,
)
Expand Down Expand Up @@ -932,10 +934,10 @@ from sap_cloud_sdk.agent_memory.factory.langgraph_checkpoint import create_check
key="config.checkpointer.ttl_seconds",
label="Thread TTL (seconds)",
description="Evict inactive conversation threads after this period of "
"inactivity. Set to 0 to disable eviction.",
"inactivity. Set to 0 to disable eviction.",
)
def thread_ttl_seconds() -> int:
return 3600 # 1 hour
return 3600 # 1 hour


class MyAgent:
Expand Down
8 changes: 5 additions & 3 deletions src/sap_cloud_sdk/agentgateway/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ from sap_cloud_sdk.agentgateway import AgentCardFilter

AgentCardFilter(
agent_names=[], # agent card names to include (matched against card JSON `name`); empty = no filter
ord_ids=[], # ORD IDs to include (extracted from fragment URL); empty = no filter
ord_ids=[], # ORD IDs to include (extracted from fragment URL); empty = no filter
)
```

Expand All @@ -261,12 +261,14 @@ Both fields default to empty lists. Filters are applied with AND semantics: if b
```python
@dataclass
class Agent:
ord_id: str # ORD ID from fragment ordId property
ord_id: str # ORD ID from fragment ordId property
agent_card: AgentCard


@dataclass
class AgentCard:
raw: dict # full parsed JSON from /.well-known/agent-card.json
raw: dict # full parsed JSON from /.well-known/agent-card.json


@dataclass
class MCPTool:
Expand Down
97 changes: 56 additions & 41 deletions src/sap_cloud_sdk/aicore/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ set_aicore_config()
from litellm import completion

response = completion(
model="sap/gpt-4",
messages=[{"role": "user", "content": "Hello!"}]
model="sap/gpt-4", messages=[{"role": "user", "content": "Hello!"}]
)
```

Expand Down Expand Up @@ -115,25 +114,31 @@ from sap_cloud_sdk.aicore import (
set_filtering,
)

set_filtering(ContentFiltering(
input_filtering=InputFiltering(filters=[
AzureContentFilter(
hate=Severity.STRICT,
violence=Severity.STRICT,
sexual=Severity.STRICT,
self_harm=Severity.STRICT,
prompt_shield=True,
set_filtering(
ContentFiltering(
input_filtering=InputFiltering(
filters=[
AzureContentFilter(
hate=Severity.STRICT,
violence=Severity.STRICT,
sexual=Severity.STRICT,
self_harm=Severity.STRICT,
prompt_shield=True,
),
]
),
]),
output_filtering=OutputFiltering(filters=[
AzureContentFilter(
hate=Severity.MEDIUM,
violence=Severity.MEDIUM,
sexual=Severity.MEDIUM,
self_harm=Severity.MEDIUM,
output_filtering=OutputFiltering(
filters=[
AzureContentFilter(
hate=Severity.MEDIUM,
violence=Severity.MEDIUM,
sexual=Severity.MEDIUM,
self_harm=Severity.MEDIUM,
),
]
),
]),
))
)
)
```

To re-apply env-based config (e.g. after changing `AICORE_FILTER_*`):
Expand Down Expand Up @@ -161,12 +166,16 @@ from sap_cloud_sdk.aicore import (
set_filtering,
)

set_filtering(ContentFiltering(
input_filtering=InputFiltering(filters=[
AzureContentFilter(prompt_shield=True),
LlamaGuard38bFilter(hate=True, violent_crimes=True),
]),
))
set_filtering(
ContentFiltering(
input_filtering=InputFiltering(
filters=[
AzureContentFilter(prompt_shield=True),
LlamaGuard38bFilter(hate=True, violent_crimes=True),
]
),
)
)
```

`LlamaGuard38bFilter` takes 14 boolean category toggles (`hate`,
Expand Down Expand Up @@ -239,15 +248,23 @@ set_filtering(hate=0, violence=0)

# After (aicore namespace with class API):
from sap_cloud_sdk.aicore import (
AzureContentFilter, ContentFiltering, InputFiltering,
Severity, ContentFilteredError, set_filtering,
AzureContentFilter,
ContentFiltering,
InputFiltering,
Severity,
ContentFilteredError,
set_filtering,
)

set_filtering(ContentFiltering(
input_filtering=InputFiltering(filters=[
AzureContentFilter(hate=Severity.STRICT, violence=Severity.STRICT),
]),
))
set_filtering(
ContentFiltering(
input_filtering=InputFiltering(
filters=[
AzureContentFilter(hate=Severity.STRICT, violence=Severity.STRICT),
]
),
)
)
```

Env vars also renamed: `ORCH_FILTER_*` → `AICORE_FILTER_*`. The
Expand Down Expand Up @@ -283,8 +300,8 @@ response = completion(
model="sap/gpt-4",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is SAP AI Core?"}
]
{"role": "user", "content": "What is SAP AI Core?"},
],
)

print(response.choices[0].message.content)
Expand All @@ -301,7 +318,7 @@ set_aicore_config()
response = completion(
model="sap/gpt-4",
messages=[{"role": "user", "content": "Tell me a story"}],
stream=True
stream=True,
)

for chunk in response:
Expand All @@ -319,14 +336,12 @@ set_aicore_config()

# Chat completion
chat_response = completion(
model="sap/gpt-4",
messages=[{"role": "user", "content": "Hello!"}]
model="sap/gpt-4", messages=[{"role": "user", "content": "Hello!"}]
)

# Text embeddings
embedding_response = embedding(
model="sap/text-embedding-ada-002",
input=["Hello world", "How are you?"]
model="sap/text-embedding-ada-002", input=["Hello world", "How are you?"]
)
```

Expand Down Expand Up @@ -356,10 +371,10 @@ try:
model="sap/gpt-4",
messages=[
{"role": "system", "content": "You are a helpful AI assistant."},
{"role": "user", "content": "Explain SAP AI Core in one sentence."}
{"role": "user", "content": "Explain SAP AI Core in one sentence."},
],
temperature=0.7,
max_tokens=100
max_tokens=100,
)

print(f"Response: {response.choices[0].message.content}")
Expand Down
Loading
Loading