From 33dae977bad5efdf19c68f307d164c4ae30940fa Mon Sep 17 00:00:00 2001 From: Patrick Kelly Date: Thu, 21 Aug 2025 14:06:39 -0400 Subject: [PATCH 1/6] content --- releases/determinism-plus-llm/README.md | 58 +++ .../determinism-plus-llm/codegen-diagram.svg | 168 +++++++++ .../kong-air-flights.yaml | 208 +++++++++++ .../determinism-plus-llm/python/CLAUDE.md | 347 ++++++++++++++++++ 4 files changed, 781 insertions(+) create mode 100644 releases/determinism-plus-llm/README.md create mode 100644 releases/determinism-plus-llm/codegen-diagram.svg create mode 100644 releases/determinism-plus-llm/kong-air-flights.yaml create mode 100644 releases/determinism-plus-llm/python/CLAUDE.md diff --git a/releases/determinism-plus-llm/README.md b/releases/determinism-plus-llm/README.md new file mode 100644 index 0000000..db91b7f --- /dev/null +++ b/releases/determinism-plus-llm/README.md @@ -0,0 +1,58 @@ +# Deterministic Codegen Meets LLM Codegen for API Client SDKs + +Traditional codegen is fast and reliable but rigid - change your API spec and you get predictable, identical output every time. LLM codegen is adaptive and intelligent but inconsistent - ask it to generate the same SDK twice and you'll get different results. + +This new hybrid approach gives you both: the reliability of deterministic generation for your core SDK structure, with LLM intelligence layered on top for adaptive features like intelligent parameter handling, context-aware documentation, and smart error recovery. + +## How it works + +The system runs deterministic codegen to establish the SDK structure. Then LLMs enhance specific components where adaptability adds value - like generating contextual examples, or adding functions that chain together multiple API calls. + +The LLM can edit most the files (see [rules](./python/CLAUDE.md)) generated by deterministic codegen, and changes will persist across regenerations. The system uses structured pattern matching queries—essentially SQL for source code syntax trees—to precisely target only the elements that need updating. Instead of overwriting entire files, it identifies specific patterns (function signatures, import statements, etc.) and surgically modifies those components while preserving all custom code around them. + +![Codegen Process](./codegen-diagram.svg) + + +The LLM component operates through rules files (like CLAUDE.md) that define what the AI can modify, and coding standards to follow. SDK builders can open the SDKs in Cursor, Claude Code, Gemini, or GH Copilot, and the LLM will follow the guidelines to enhance the code. (check out the python sdk claude code rules file here: [CLAUDE.md](./python/CLAUDE.md)) + +## Get started with your API + +### Choose a method to install the sideko cli +- `npm install -g @sideko/cli` +- `pip install sideko-py` +- `brew install sideko-inc/tap/sideko` +- `curl -fsSL https://raw.githubusercontent.com/Sideko-Inc/sideko/main/install.sh | sh` + +### Login (social or email) +`sideko login` + +### Create initial sdk +`sideko sdk init` + +(*NOTE: use this example OpenAPI if you do not have one to test with: [Kong Air Flights API](./kong-air-flights.yaml)*) + +(*NOTE: if your OpenAPI spec has lot's of linting errors, try pasting the result of `sideko api lint --spec ./your-openapi.yml --errors` into your favorite LLM IDE*) + +### Get prompting + +Sample prompt for the example api +``` +create a flight tracking workflow: + 1. get all flights + 2. select the next flight + 3. return "enjoy :)" if the flight has in-flight entertainment +``` + +### Push your changes +When all tests are passing, push your code + +### Set up automatic openapi -> sdk sync +- push sdk to github +- setup automatic updates with the [official github action](https://github.com/marketplace/actions/sideko-sdk-update) + +### What's next +Join our slack +Unlock a second SDK language in your free trial [company linkedin](https://www.linkedin.com/company/sideko/) +We will be adding support for other languages +New language, SDK customization CLI generation, Bundled SDKs, API Docs, etc. ,eeting link +Explore our pricing model diff --git a/releases/determinism-plus-llm/codegen-diagram.svg b/releases/determinism-plus-llm/codegen-diagram.svg new file mode 100644 index 0000000..75a04b3 --- /dev/null +++ b/releases/determinism-plus-llm/codegen-diagram.svg @@ -0,0 +1,168 @@ + + + + + + + + + + + Original Code + + + + + import + json + + def + custom_logger + (msg): + # Custom logging logic + print + ( + f"LOG: {msg}" + ) + + class + APIClient + : + + + + def + send_request + (self, data): + return + json.dumps(data) + + def + validate_response + (self, resp): + # Custom validation + return + resp + is not None + + + + + + + AST Node Updates + + + + + // AST node manipulation + MATCH + function + 'send_request' + GET + params_list = node.args + + APPEND + to params_list: + + ast.arg(arg= + 'timeout' + , + default=ast.Constant(30)) + + UPDATE + function_body + REPLACE + 'json.dumps(data)' + WITH + + 'json.dumps(data, timeout=timeout)' + + + + + + + Result + + + + + import + json + + def + custom_logger + (msg): + # Custom logging logic + print + ( + f"LOG: {msg}" + ) + + class + APIClient + : + + + + def + send_request + (self, data, + timeout=30): + return + json.dumps(data, + timeout=timeout) + + def + validate_response + (self, resp): + # Custom validation + return + resp + is not None + \ No newline at end of file diff --git a/releases/determinism-plus-llm/kong-air-flights.yaml b/releases/determinism-plus-llm/kong-air-flights.yaml new file mode 100644 index 0000000..8b47dcd --- /dev/null +++ b/releases/determinism-plus-llm/kong-air-flights.yaml @@ -0,0 +1,208 @@ +--- +# OpenAPI Spec for the KongAir Flights service +openapi: 3.0.0 + +info: + description: KongAir Flights service provides the scheduled flights for KongAir + version: 0.1.0 + title: Flights Service + +servers: +- url: https://api.kong-air.com + description: KongAir API Server + +paths: + /health: + get: + summary: Health check endpoint + description: Endpoint that returns the service health status. + operationId: flights-health-check + responses: + '200': + description: Service is healthy + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: "OK" + headers: + hostname: + description: "The hostname of the machine fulfilling the request." + schema: + type: string + '500': + description: Service is unhealthy + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: "unhealthy" + "/flights": + get: + summary: Get KongAir planned flights + description: | + Returns all the scheduled flights for a given day + tags: + - flight-data + operationId: get-flights + parameters: + - name: date + in: query + description: Filter by date (defaults to current day) + required: false + style: form + schema: + type: string + format: date + responses: + '200': + description: Successful respone with scheduled flights + headers: + hostname: + description: "The hostname of the machine fulfilling the request." + schema: + type: string + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Flight' + examples: + Example Flights List: + value: + - number: "KD924" + route_id: "LHR-SFO" + scheduled_departure: "2024-03-20T09:12:28Z" + scheduled_arrival: "2024-03-20T19:12:28Z" + - number: "KD925" + route_id: "SFO-LHR" + scheduled_departure: "2024-03-21T09:12:28Z" + scheduled_arrival: "2024-03-21T19:12:28Z" + + "/flights/{flightNumber}": + get: + summary: Get a specific flight by flight number + description: | + Returns a specific flight given its flight number + tags: + - flight-data + operationId: get-flight-by-number + parameters: + - name: flightNumber + in: path + description: The flight number + required: true + style: simple + schema: + type: string + responses: + '200': + description: Successful response with the requested flight + headers: + hostname: + description: "The hostname of the machine fulfilling the request." + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/Flight' + examples: + Example Flight KD924: + value: + number: "KD924" + route_id: "LHR-SFO" + scheduled_departure: "2024-03-20T09:12:28Z" + scheduled_arrival: "2024-03-20T19:12:28Z" + '404': + description: Flight not found + content: + application/json: + schema: + type: object + properties: + message: + type: string + + "/flights/{flightNumber}/details": + get: + summary: Fetch more details about a flight + description: Fetch more details about a flight + tags: + - flight-data + operationId: get-flight-details + parameters: + - name: flightNumber + in: path + description: The flight number + required: true + style: simple + schema: + type: string + responses: + '200': + description: Successful response with the requested flight details + headers: + hostname: + description: "The hostname of the machine fulfilling the request." + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/FlightDetails' + '404': + description: Flight not found + content: + application/json: + schema: + type: object + properties: + message: + type: string + +components: + schemas: + Flight: + type: object + properties: + number: + type: string + route_id: + type: string + scheduled_departure: + type: string + format: date-time + scheduled_arrival: + type: string + format: date-time + required: + - number + - route_id + - scheduled_departure + - scheduled_arrival + FlightDetails: + type: object + properties: + flight_number: + type: string + in_flight_entertainment: + type: boolean + meal_options: + type: array + items: + type: string + aircraft_type: + type: string + required: + - flight_number + - in_flight_entertainment + - meal_options + - aircraft_type \ No newline at end of file diff --git a/releases/determinism-plus-llm/python/CLAUDE.md b/releases/determinism-plus-llm/python/CLAUDE.md new file mode 100644 index 0000000..29304db --- /dev/null +++ b/releases/determinism-plus-llm/python/CLAUDE.md @@ -0,0 +1,347 @@ +## Bash Commands +- `poetry install`: Install the project and the dependencies +- `poetry run pytest`: Run the testing suite +- `poetry run mypy /`: Run the type checker on the main module +- `poetry run pytest tests/test_specific_file.py -v`: Run specific test file +- `poetry add (dependency_name)`: Add a new dependency + +## Definitions +- "the client": The main exported module e.g. `from my_sdk import MyClient` +- "client function": An SDK function that the client can access +- "base client" The sync and async client in `client.py` at the root of the main module + +## Code style +- All imports should be placed at the top of a file +- Add a type hint to every function argument +- Add a type hint for every function response +- Create a sync and an async version of all client functions + +## Types Guide +- bool, int, float, str for primitives +- typing_extensions.Literal["option1", "option2"] for string enums +- typing.List[T] for arrays +- typing.Union[Type1, Type2, ...] for unions +- "MyClass" (quoted) for self-referencing objects + +### Argument Types Guide +- Use `httpx._types.FileTypes` for binary data +- For object inputs, create a typed dict class in `types/params` (see [typed-dicts](#typeddict)) and import it and use it like this: `param_name: params.MyExampleParam` +- Optional/Nullable: Wrap in typing.Optional[T] + +### Response Types Guide +- `BinaryResponse` (imported from core) for binary data +- For object inputs, create a pydantic model class in `types/models` (see [pydantic model](#pydantic-model)) and use it like this: `param_name: models.MyExampleModel` + +## Client function signatures +- See [function signature example](#function-signature) +- Start with "self, *" to prevent positional args +- The last arg should always be `request_options: typing.Optional[RequestOptions] = None,` +- Complex input types go in the `types/params` folder. These are always typed dicts +- Complex response types go in the `types/models/` folder. These are always pydantic models + +## Client functions +- For functions that combine multiple API calls, import and instantiate the specific client classes rather than trying to access them through the base client +- Example: `from module.resources.api.spec.client import SpecClient; spec_client = SpecClient(base_client=self._base_client)` +- Include appropriate `auth_names` list +- Use `cast_to=type(None)` for methods returning None + +## Tests +- Create tests for any new functionality in `tests/` +- Tests run against a stateless mock server, so never make any stateful assertions in the tests +- Create contract tests that tests that the request is successful when all parameters are given and when only required parameters are given +- Test behavior when all parameters test - Call method with all possible parameters +- Test behavior when all only required parameters test - Call method with only required parameters +- Always create Async versions using @pytest.mark.asyncio + + +## Documentation +- When creating a new resource, include a README file in the resource folder +- See the [example readme](#documentation) for the style +- Add an internal link in the README.md file at the root of the repository + +## Workflow +- Always typecheck when you're done making a series of code changes +- Prefer running single tests, and not the whole test suite +- Reference the README.md in the root of the repo to understand the module structure of the repo when gathering context. Follow the internal README links to view parameter tables, example snippets, and response examples. +- When adding a new resource: create a new folder within `resources/` create `client.py`, create `__init__.py` create `README.md` add the resource to the sync and async base clients. +- When creating new typed dicts or models, remember to add them to the respective `__init__.py` files in both the import section and `__all__` list +- Always run tests after creating new functionality to ensure everything works correctly + +## IMPORTANT RULES +- Do not remove or change versions of pydantic, httpx in pyproject.toml +- Instantiate the specific client classes needed rather than trying to access them. Never access the base_client directly +- Prefer creating a new resource when chaining together multiple API calls +- Remove unused imports to avoid linting errors +- When creating new models and params, always create new files (1 type per file) +- Always verify the correct Environment enum values before using in tests, it will usually be `Environment.MOCK_SERVER` +- When adding to an existing resource's README.md, add new content and comment so that the code generator can retain the content in future versions. + +## Code Examples + +### TypedDict +```py +import pydantic +import typing_extensions + + +class Example(typing_extensions.TypedDict): + """ + Example type description + """ + + name: typing_extensions.Required[str] + """ + A great description of the name parameter on this example typed dict + """ + + +class _SerializerExample(pydantic.BaseModel): + """ + Serializer for Example handling case conversions + and file omissions as dictated by the API + """ + + model_config = pydantic.ConfigDict( + populate_by_name=True, + ) + + name: str = pydantic.Field( + alias="name", + ) +``` + +### Pydantic Model +```py +import pydantic + + +class ModelExample(pydantic.BaseModel): + """ + A description of the purpose of this example model + """ + + model_config = pydantic.ConfigDict( + arbitrary_types_allowed=True, + populate_by_name=True, + ) + + name: str = pydantic.Field( + alias="name", + ) + """ + A great description of the name field on this example model + """ +``` + +### Function signature +```py + def create( + self, + *, + asset: params.Asset, + name: typing.Union[ + typing.Optional[str], type_utils.NotGiven + ] = type_utils.NOT_GIVEN, + request_options: typing.Optional[RequestOptions] = None, + ) -> models.CreatedAssets: +``` + +### New resource +Example file location: `resources/your_resource/__init__.py` +```python +from .client import AsyncYourResourceClient, YourResourceClient + +__all__ = ["AsyncYourResourceClient", "YourResourceClient"] +``` + +Example file location: `resources/your_resource/client.py` +```py +import typing +from .core import ( + AsyncBaseClient, + RequestOptions, + SyncBaseClient, + type_utils, +) +from .types import models + +from .resources.custom import CustomClient + +class ResourceClient: + def __init__(self, *, base_client: SyncBaseClient) -> None: + self._base_client = base_client + + def your_new_method( + self, + *, + required_param: str, + optional_param: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None + ) -> models.NewMethodResponse: + """ + Brief description + + Longer description explaining the functionality. + + HTTP_METHOD /api/endpoint/path + + Args: + required_param: Description of required parameter + optional_param: Description of optional parameter + request_options: Additional options to customize the HTTP request + + Returns: + Success response description + + Raises: + ApiError: A custom exception class that provides additional context + for API errors, including the HTTP status code and response body. + """ + your_client = CustomClient(base_client=self._base_client) + result = your_client.call_api( + required_param=param1, + optional_param=optional_param, + request_options=request_options + ) + return models.NewMethodResponse(data=result) + +class AsyncYourResourceClient: + def __init__(self, *, base_client: AsyncBaseClient) -> None: + self._base_client = base_client + + async def your_new_method( + self, + *, + required_param: str, + optional_param: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None + ) -> models.NewMethodResponse: + """ + [Same docstring as sync version, but explain that it is async] + """ + your_client = AsyncCustomClient(base_client=self._base_client) + result = await your_client.call_api( + required_param=param1, + optional_param=optional_param, + request_options=request_options + ) + return models.NewMethodResponse(data=result) +``` + + +### Tests +```python +import io +import pydantic +import pytest +import typing + +from import AsyncResourceClient, ResourceClient +from .environment import Environment +from .types import models + + +def test_create_201_success_all_params(): + """Tests a POST request to the /project/{name} endpoint. + + Operation: create + Test Case ID: success_all_params + Expected Status: 201 + Mode: Synchronous execution + + Empty response expected + + Validates: + - Authentication requirements are satisfied + - All required input parameters are properly handled + - Response status code is correct + - Response data matches expected schema + + This test uses example data to verify the endpoint behavior. + """ + # tests calling sync method with example data + client = ResourceClient(api_key="API_KEY", environment=Environment.MOCK_SERVER) + response = client.resource.create(project="my-project", file=io.BytesIO(b"123")) + try: + pydantic.TypeAdapter(models.Project).validate_python(response) + is_valid_response_schema = True + except pydantic.ValidationError: + is_valid_response_schema = False + assert is_valid_response_schema, "failed response type check" + + +@pytest.mark.asyncio +async def test_await_create_201_success_all_params(): + """Tests a POST request to the /project/{name} endpoint. + + Operation: create + Test Case ID: success_all_params + Expected Status: 201 + Mode: Asynchronous execution + + Empty response expected + + Validates: + - Authentication requirements are satisfied + - All required input parameters are properly handled + - Response status code is correct + - Response data matches expected schema + + This test uses example data to verify the endpoint behavior. + """ + # tests calling async method with example data + client = ResourceClient(api_key="API_KEY", environment=Environment.MOCK_SERVER) + response = await client.resource.create(project="my-project", file=io.BytesIO(b"123")) + try: + pydantic.TypeAdapter(models.Project).validate_python(response) + is_valid_response_schema = True + except pydantic.ValidationError: + is_valid_response_schema = False + assert is_valid_response_schema, "failed response type check" + +``` + + +### Documentation +```markdown +### Method Name + +Brief description of what the method does. + +**API Endpoint**: `HTTP_METHOD /api/endpoint/path` (or all endpoints it will hit if it's a workflow) + +#### Parameters + +| Parameter | Required | Description | Example | +|-----------|:--------:|-------------|---------| +| `required_param` | ✓ | Description | `"example_value"` | +| `optional_param` | | Description | `"optional_value"` | + +#### Synchronous Client + +```python +from import +from os import getenv + +client = (api_key=getenv("API_KEY")) +res = client.your_resource.method_name(required_param="value") +``` + +#### Asynchronous Client + +```python +from import +from os import getenv + +client = (api_key=getenv("API_KEY")) +res = await client.your_resource.method_name(required_param="value") +``` + +#### Response + +##### Type +[YourResponseModel](/path/to/types/your_response_model.py) + +##### Example +`{"field": "value", "another_field": 123}` +``` \ No newline at end of file From 64cf2f35479132e98314ed35801dcc9468f3d652 Mon Sep 17 00:00:00 2001 From: Patrick Kelly Date: Fri, 22 Aug 2025 09:31:42 -0400 Subject: [PATCH 2/6] release prep --- releases/determinism-plus-llm/README.md | 17 +- .../determinism-plus-llm/python/CLAUDE.md | 17 +- .../determinism-plus-llm/typescript/CLAUDE.md | 212 ++++++++++++++++++ 3 files changed, 228 insertions(+), 18 deletions(-) create mode 100644 releases/determinism-plus-llm/typescript/CLAUDE.md diff --git a/releases/determinism-plus-llm/README.md b/releases/determinism-plus-llm/README.md index db91b7f..1c8c3ae 100644 --- a/releases/determinism-plus-llm/README.md +++ b/releases/determinism-plus-llm/README.md @@ -8,7 +8,7 @@ This new hybrid approach gives you both: the reliability of deterministic genera The system runs deterministic codegen to establish the SDK structure. Then LLMs enhance specific components where adaptability adds value - like generating contextual examples, or adding functions that chain together multiple API calls. -The LLM can edit most the files (see [rules](./python/CLAUDE.md)) generated by deterministic codegen, and changes will persist across regenerations. The system uses structured pattern matching queries—essentially SQL for source code syntax trees—to precisely target only the elements that need updating. Instead of overwriting entire files, it identifies specific patterns (function signatures, import statements, etc.) and surgically modifies those components while preserving all custom code around them. +The LLM can edit most the files (see [python rules](./python/CLAUDE.md) [typescript rules](./typescript/CLAUDE.md)) generated by deterministic codegen, and changes will persist across regenerations. The system uses structured pattern matching queries—essentially SQL for source code syntax trees—to precisely target only the elements that need updating. Instead of overwriting entire files, it identifies specific patterns (function signatures, import statements, etc.) and surgically modifies those components while preserving all custom code around them. ![Codegen Process](./codegen-diagram.svg) @@ -35,12 +35,12 @@ The LLM component operates through rules files (like CLAUDE.md) that define what ### Get prompting -Sample prompt for the example api +Sample prompt for the example api above ``` create a flight tracking workflow: 1. get all flights 2. select the next flight - 3. return "enjoy :)" if the flight has in-flight entertainment + 3. return "enjoy :)" if the flight has in-flight entertainment and ":(" if not ``` ### Push your changes @@ -48,11 +48,10 @@ When all tests are passing, push your code ### Set up automatic openapi -> sdk sync - push sdk to github -- setup automatic updates with the [official github action](https://github.com/marketplace/actions/sideko-sdk-update) +- (LINK to new docs) ### What's next -Join our slack -Unlock a second SDK language in your free trial [company linkedin](https://www.linkedin.com/company/sideko/) -We will be adding support for other languages -New language, SDK customization CLI generation, Bundled SDKs, API Docs, etc. ,eeting link -Explore our pricing model +- join our slack: (LINK) +- post your SDK and tag us on [linkedin](https://www.linkedin.com/company/sideko/) to show support +- adding llm enhancement support to rust, go, jav, and c# soon +- explore [pricing](https://sideko.dev/pricing) diff --git a/releases/determinism-plus-llm/python/CLAUDE.md b/releases/determinism-plus-llm/python/CLAUDE.md index 29304db..7515592 100644 --- a/releases/determinism-plus-llm/python/CLAUDE.md +++ b/releases/determinism-plus-llm/python/CLAUDE.md @@ -8,12 +8,12 @@ ## Definitions - "the client": The main exported module e.g. `from my_sdk import MyClient` - "client function": An SDK function that the client can access -- "base client" The sync and async client in `client.py` at the root of the main module +- "root client" The sync and async client in `client.py` at the root of the main module ## Code style - All imports should be placed at the top of a file - Add a type hint to every function argument -- Add a type hint for every function response +- Add a return type hint to every function - Create a sync and an async version of all client functions ## Types Guide @@ -26,11 +26,10 @@ ### Argument Types Guide - Use `httpx._types.FileTypes` for binary data - For object inputs, create a typed dict class in `types/params` (see [typed-dicts](#typeddict)) and import it and use it like this: `param_name: params.MyExampleParam` -- Optional/Nullable: Wrap in typing.Optional[T] ### Response Types Guide - `BinaryResponse` (imported from core) for binary data -- For object inputs, create a pydantic model class in `types/models` (see [pydantic model](#pydantic-model)) and use it like this: `param_name: models.MyExampleModel` +- For object responses, create a pydantic model class in `types/models` (see [pydantic model](#pydantic-model)) and use it like this: `models.MyExampleModel` ## Client function signatures - See [function signature example](#function-signature) @@ -40,7 +39,7 @@ - Complex response types go in the `types/models/` folder. These are always pydantic models ## Client functions -- For functions that combine multiple API calls, import and instantiate the specific client classes rather than trying to access them through the base client +- For functions that combine multiple API calls, import and instantiate specific client classes - Example: `from module.resources.api.spec.client import SpecClient; spec_client = SpecClient(base_client=self._base_client)` - Include appropriate `auth_names` list - Use `cast_to=type(None)` for methods returning None @@ -49,7 +48,6 @@ - Create tests for any new functionality in `tests/` - Tests run against a stateless mock server, so never make any stateful assertions in the tests - Create contract tests that tests that the request is successful when all parameters are given and when only required parameters are given -- Test behavior when all parameters test - Call method with all possible parameters - Test behavior when all only required parameters test - Call method with only required parameters - Always create Async versions using @pytest.mark.asyncio @@ -57,7 +55,6 @@ ## Documentation - When creating a new resource, include a README file in the resource folder - See the [example readme](#documentation) for the style -- Add an internal link in the README.md file at the root of the repository ## Workflow - Always typecheck when you're done making a series of code changes @@ -70,10 +67,12 @@ ## IMPORTANT RULES - Do not remove or change versions of pydantic, httpx in pyproject.toml - Instantiate the specific client classes needed rather than trying to access them. Never access the base_client directly -- Prefer creating a new resource when chaining together multiple API calls +- Prefer creating a new resource when chaining together multiple API calls - Remove unused imports to avoid linting errors - When creating new models and params, always create new files (1 type per file) -- Always verify the correct Environment enum values before using in tests, it will usually be `Environment.MOCK_SERVER` +- Do not change any code in `/core` +- Do not change the code in `/environment.py` +- Always use the correct Environment enum value in tests (`Environment.MOCK_SERVER`) - When adding to an existing resource's README.md, add new content and comment so that the code generator can retain the content in future versions. ## Code Examples diff --git a/releases/determinism-plus-llm/typescript/CLAUDE.md b/releases/determinism-plus-llm/typescript/CLAUDE.md new file mode 100644 index 0000000..627ef71 --- /dev/null +++ b/releases/determinism-plus-llm/typescript/CLAUDE.md @@ -0,0 +1,212 @@ +## Bash Commands +- `npm i`: Install the project and the dependencies +- `npm test`: Run the entire testing suite +- `npm test -- --testNamePattern="client.exampleResource.exampleMethod"` +- `npm add (dependency_name)`: Add a new dependency + +## Definitions +- "the client": The main exported module e.g. `import MyClient from "my_sdk"` +- "client function": An SDK function that the client can access +- "root client": `client.ts` at the root of the main module + +## Code style +- All imports should be placed at the top of a file +- Add a type to every function argument +- Add a return type to every function +- Add a docstring to every new function + +## Types Guide +- boolean, number, string for primitives +- string literals with "|" for string enums +- [] for arrays +- "|" for unions + +### Argument Types Guide +- `import { type UploadFile } from "/core";` for binary data +- For object inputs, create a file called `argument-types.ts` at the same level as the client and export the created type from `index.ts` + +### Response Types Guide +- `import { BinaryResponse } from "/core";` for binary data +- For object responses, create a new file and type in the `src/types` folder. +- Export any newly created types from `src/types/index.ts` + +## Client functions +- Functions should take at max two inputs. The first is a type for input data, and the second is `opts?: RequestOptions` (`import { RequestOptions} from "/core";`) + +## Tests +- Create jest test for any new functionality in `test/` +- Tests run against a stateless mock server, so never make any stateful assertions in the tests +- Create contract tests that test that the request is successful when all parameters are given and when only required parameters are given + +## Documentation +- When creating a new resource, include a README file in the resource folder +- See the [example readme](#documentation) for the style + +## IMPORTANT RULES +- Do not remove or change the versions of `form-data` `form-url-encoded` `js-base64` `jsonpointer` `node-fetch` `qs` or `zod` +- Any imports that are node or browser dependent must be imported using the `require` keyword inline according to the runtime from (`import { RUNTIME } from "/core";`) +- Prefer creating a new resource when chaining together multiple API calls +- Do not change any code in `"src/core"`; +- Do not change `src/environment.ts` +- Never use this._client.makeRequest directly, always use existing functionalities via resource clients + +## Code Examples + +### Type +```ts +import * as z from "zod"; + +import { zodTransform } from "/portal-client/core"; + +/** + * Example Type + */ +export type Example = { + /** + * the unique identifier of this example + */ + id: string; +}; +``` + +### Function Signature + /** + * Functionality Description + */ + method( + request: requests.MethodRequest, + opts?: RequestOptions, + ): Promise { + } + +### New Resource +Files +- index: `src/resources/example-resource/index.ts` +- client: `src/resources/example-resource/resource-client.ts` +- request types: `src/resources/example-resource/request-types.ts` +- docs: `src/resources/example-resource/README.md` + +Example `index.ts` +```ts +export { + ExampleMethodRequest, +} from "./request-types"; +export { ExampleClient } from "./resource-client"; +``` + +Example `request-types.ts` +```ts +export type ExampleMethodRequest = { + id: string; + val?: string; +} +``` + +Example `resource-client.ts` +```ts +import type { types } from ""; +import { + CoreResourceClient, + type RequestOptions, +} from "/core"; +import type * as requests from "/resources/example-resource/request-types"; +import { ResourceClientA } from "/resources/a"; +import { ResourceClientB } from "/resources/b"; + + +export class ExampleClient extends CoreResourceClient { + /** + * Example Description + */ + async exampleMethod( + request: requests.ExampleMethodRequest = {}, + opts?: RequestOptions, + ): Promise { + const resourceA = new ResourceClientA(this._client, this._opts); + const resourceB = new ResourceClientB(this._client, this._opts); + + const resA = await resourceA.get({ id: request.id }); + const resB = await resourceB.create({ a: resA.id, val: request.val }); + + return { aId: resA.id, bId: resB.id }; + } +} + +``` + +### Tests +`test/example-resource.test.ts` +```ts +import Client, { Environment } from ""; + +describe("tests client.exampleResource.exampleMethod", () => { + test.concurrent( + "Desciption actions | testId: success_all_params | Desciption of test", + async () => { + const client = new Client({ + apiKey: "API_KEY", + environment: Environment.MockServer, + }); + const response = await client.exampleResource.exampleMethod( + { + id: "abc", + val: "data", + } + ); + expect(response).toBeDefined(); + } + ) + test.concurrent( + "Desciption actions | testId: success_required_only | Desciption of test", + async () => { + const client = new Client({ + apiKey: "API_KEY", + environment: Environment.MockServer, + }); + const response = await client.exampleResource.exampleMethod({ id: "abc" }); + expect(response).toBeDefined(); + } + ) +}); +``` + +### Documentation +`src/resources/example-resource/README.md` + +````markdown +# example-resource + +## Module Functions +### Example Method + +#### Parameters + +| Parameter | Required | Description | Example | +|-----------|:--------:|-------------|--------| +| `id` | ✓ | the unique idenifier | `"3e4666bf-d5e5-4aa7-b8ce-cefe41c7568a"` | +| `val` | ✗ | the example to send | `data` | + + +#### Example Snippet + +```typescript +import Client from ""; + +const client = new Client({ + apiKey: process.env["API_KEY"]!!, +}); +const res = await client.exampleResource.exampleMethod({ + id: "3e4666bf-d5e5-4aa7-b8ce-cefe41c7568a" +}); + +``` + +#### Response + +##### Type +[ExampleMethodOutput](/src/types/example-method-output.ts) + +##### Example +`{ aId: "abc", bId: "bcd" }` + +```` \ No newline at end of file From 20a719045966209f51e0cea02e7665acc20f1a4f Mon Sep 17 00:00:00 2001 From: Patrick Kelly Date: Fri, 22 Aug 2025 12:18:56 -0400 Subject: [PATCH 3/6] sync with latest + updates --- releases/determinism-plus-llm/README.md | 33 ++++++++++++------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/releases/determinism-plus-llm/README.md b/releases/determinism-plus-llm/README.md index 1c8c3ae..03c8bac 100644 --- a/releases/determinism-plus-llm/README.md +++ b/releases/determinism-plus-llm/README.md @@ -6,18 +6,21 @@ This new hybrid approach gives you both: the reliability of deterministic genera ## How it works +Don't care how it works? Skip to: [get started](#get-started-with-your-apis) + The system runs deterministic codegen to establish the SDK structure. Then LLMs enhance specific components where adaptability adds value - like generating contextual examples, or adding functions that chain together multiple API calls. -The LLM can edit most the files (see [python rules](./python/CLAUDE.md) [typescript rules](./typescript/CLAUDE.md)) generated by deterministic codegen, and changes will persist across regenerations. The system uses structured pattern matching queries—essentially SQL for source code syntax trees—to precisely target only the elements that need updating. Instead of overwriting entire files, it identifies specific patterns (function signatures, import statements, etc.) and surgically modifies those components while preserving all custom code around them. +The LLM can edit most the files (see [python rules](./python/CLAUDE.md) and [typescript rules](./typescript/CLAUDE.md)) generated by deterministic codegen, and changes will persist across regenerations. The system uses structured pattern matching queries—essentially SQL for source code syntax trees—to precisely target only the elements that need updating. Instead of overwriting entire files, it identifies specific patterns (function signatures, import statements, etc.) and surgically modifies those components while preserving all custom code around them. ![Codegen Process](./codegen-diagram.svg) +Questions? Join our new [community slack](https://join.slack.com/t/sideko-community/shared_invite/zt-3bx0d66ra-iUN69c6qwcd2rnQ5BCY7zQ). -The LLM component operates through rules files (like CLAUDE.md) that define what the AI can modify, and coding standards to follow. SDK builders can open the SDKs in Cursor, Claude Code, Gemini, or GH Copilot, and the LLM will follow the guidelines to enhance the code. (check out the python sdk claude code rules file here: [CLAUDE.md](./python/CLAUDE.md)) +The LLM component operates through rules files (like CLAUDE.md) that define what the AI can modify, and coding standards to follow. SDK builders can open the SDKs in Cursor, Claude Code, Gemini, or GH Copilot, and the LLM will follow the guidelines to enhance the code. -## Get started with your API +## Get started with your APIs -### Choose a method to install the sideko cli +### Pick an install method to get `sideko` cli - `npm install -g @sideko/cli` - `pip install sideko-py` - `brew install sideko-inc/tap/sideko` @@ -29,29 +32,25 @@ The LLM component operates through rules files (like CLAUDE.md) that define what ### Create initial sdk `sideko sdk init` -(*NOTE: use this example OpenAPI if you do not have one to test with: [Kong Air Flights API](./kong-air-flights.yaml)*) - -(*NOTE: if your OpenAPI spec has lot's of linting errors, try pasting the result of `sideko api lint --spec ./your-openapi.yml --errors` into your favorite LLM IDE*) +(*NOTE: use this example OpenAPI if you do not have one: [Flights API](./kong-air-flights.yaml)*) -### Get prompting +### Start prompting -Sample prompt for the example api above +Sample prompt for the example api: ``` -create a flight tracking workflow: +create a flight tracking workflow 1. get all flights 2. select the next flight 3. return "enjoy :)" if the flight has in-flight entertainment and ":(" if not ``` ### Push your changes -When all tests are passing, push your code +Once all tests are passing, push your code to github -### Set up automatic openapi -> sdk sync -- push sdk to github +### Set up automatic openapi-to-sdk syncing - (LINK to new docs) ### What's next -- join our slack: (LINK) -- post your SDK and tag us on [linkedin](https://www.linkedin.com/company/sideko/) to show support -- adding llm enhancement support to rust, go, jav, and c# soon -- explore [pricing](https://sideko.dev/pricing) +- [join our brand new slack](https://join.slack.com/t/sideko-community/shared_invite/zt-3bx0d66ra-iUN69c6qwcd2rnQ5BCY7zQ) for questions + sharing your work +- we will be adding llm enhancement support to rust, go, java, and c# in the coming weeks +- explore other sideko features like [beautiful api docs](https://docs.sideko.dev/building-documentation/getting-started) From b4cbc14a269b8e8203ff702d80d6fd74ce350df6 Mon Sep 17 00:00:00 2001 From: Patrick Kelly Date: Fri, 22 Aug 2025 12:25:55 -0400 Subject: [PATCH 4/6] updates --- .../README.md | 8 ++++---- .../assets}/codegen-diagram.svg | 0 .../assets}/kong-air-flights.yaml | 3 ++- .../assets}/python/CLAUDE.md | 0 .../assets}/typescript/CLAUDE.md | 0 5 files changed, 6 insertions(+), 5 deletions(-) rename releases/{determinism-plus-llm => determinism-plus-llms}/README.md (74%) rename releases/{determinism-plus-llm => determinism-plus-llms/assets}/codegen-diagram.svg (100%) rename releases/{determinism-plus-llm => determinism-plus-llms/assets}/kong-air-flights.yaml (96%) rename releases/{determinism-plus-llm => determinism-plus-llms/assets}/python/CLAUDE.md (100%) rename releases/{determinism-plus-llm => determinism-plus-llms/assets}/typescript/CLAUDE.md (100%) diff --git a/releases/determinism-plus-llm/README.md b/releases/determinism-plus-llms/README.md similarity index 74% rename from releases/determinism-plus-llm/README.md rename to releases/determinism-plus-llms/README.md index 03c8bac..e123ae8 100644 --- a/releases/determinism-plus-llm/README.md +++ b/releases/determinism-plus-llms/README.md @@ -10,9 +10,9 @@ Don't care how it works? Skip to: [get started](#get-started-with-your-apis) The system runs deterministic codegen to establish the SDK structure. Then LLMs enhance specific components where adaptability adds value - like generating contextual examples, or adding functions that chain together multiple API calls. -The LLM can edit most the files (see [python rules](./python/CLAUDE.md) and [typescript rules](./typescript/CLAUDE.md)) generated by deterministic codegen, and changes will persist across regenerations. The system uses structured pattern matching queries—essentially SQL for source code syntax trees—to precisely target only the elements that need updating. Instead of overwriting entire files, it identifies specific patterns (function signatures, import statements, etc.) and surgically modifies those components while preserving all custom code around them. +The LLM can edit most the files (see [python rules](./assets/python/CLAUDE.md) and [typescript rules](./assets/typescript/CLAUDE.md)) generated by deterministic codegen, and changes will persist across regenerations. The system uses structured pattern matching queries—essentially SQL for source code syntax trees—to precisely target only the elements that need updating. Instead of overwriting entire files, it identifies specific patterns (function signatures, import statements, etc.) and surgically modifies those components while preserving all custom code around them. -![Codegen Process](./codegen-diagram.svg) +![Codegen Process](./assets/codegen-diagram.svg) Questions? Join our new [community slack](https://join.slack.com/t/sideko-community/shared_invite/zt-3bx0d66ra-iUN69c6qwcd2rnQ5BCY7zQ). @@ -32,7 +32,7 @@ The LLM component operates through rules files (like CLAUDE.md) that define what ### Create initial sdk `sideko sdk init` -(*NOTE: use this example OpenAPI if you do not have one: [Flights API](./kong-air-flights.yaml)*) +(*NOTE: use this example OpenAPI if you do not have one: [Flights API](./assets/kong-air-flights.yaml)*) ### Start prompting @@ -51,6 +51,6 @@ Once all tests are passing, push your code to github - (LINK to new docs) ### What's next -- [join our brand new slack](https://join.slack.com/t/sideko-community/shared_invite/zt-3bx0d66ra-iUN69c6qwcd2rnQ5BCY7zQ) for questions + sharing your work +- [join our new slack](https://join.slack.com/t/sideko-community/shared_invite/zt-3bx0d66ra-iUN69c6qwcd2rnQ5BCY7zQ) for questions + sharing your work - we will be adding llm enhancement support to rust, go, java, and c# in the coming weeks - explore other sideko features like [beautiful api docs](https://docs.sideko.dev/building-documentation/getting-started) diff --git a/releases/determinism-plus-llm/codegen-diagram.svg b/releases/determinism-plus-llms/assets/codegen-diagram.svg similarity index 100% rename from releases/determinism-plus-llm/codegen-diagram.svg rename to releases/determinism-plus-llms/assets/codegen-diagram.svg diff --git a/releases/determinism-plus-llm/kong-air-flights.yaml b/releases/determinism-plus-llms/assets/kong-air-flights.yaml similarity index 96% rename from releases/determinism-plus-llm/kong-air-flights.yaml rename to releases/determinism-plus-llms/assets/kong-air-flights.yaml index 8b47dcd..7fe183b 100644 --- a/releases/determinism-plus-llm/kong-air-flights.yaml +++ b/releases/determinism-plus-llms/assets/kong-air-flights.yaml @@ -1,5 +1,6 @@ --- -# OpenAPI Spec for the KongAir Flights service +# download this file for sdk generation: +# curl -o openapi.yaml https://raw.githubusercontent.com/Sideko-Inc/sideko/refs/heads/main/releases/determinism-plus-llm/kong-air-flights.yaml openapi: 3.0.0 info: diff --git a/releases/determinism-plus-llm/python/CLAUDE.md b/releases/determinism-plus-llms/assets/python/CLAUDE.md similarity index 100% rename from releases/determinism-plus-llm/python/CLAUDE.md rename to releases/determinism-plus-llms/assets/python/CLAUDE.md diff --git a/releases/determinism-plus-llm/typescript/CLAUDE.md b/releases/determinism-plus-llms/assets/typescript/CLAUDE.md similarity index 100% rename from releases/determinism-plus-llm/typescript/CLAUDE.md rename to releases/determinism-plus-llms/assets/typescript/CLAUDE.md From 04c81acdde624f04cfcbb844564660ed4bce92f6 Mon Sep 17 00:00:00 2001 From: Patrick Kelly Date: Sun, 24 Aug 2025 16:37:13 -0400 Subject: [PATCH 5/6] links to request libs --- releases/determinism-plus-llms/README.md | 32 ++++++++++++++++-------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/releases/determinism-plus-llms/README.md b/releases/determinism-plus-llms/README.md index e123ae8..de0570c 100644 --- a/releases/determinism-plus-llms/README.md +++ b/releases/determinism-plus-llms/README.md @@ -1,4 +1,4 @@ -# Deterministic Codegen Meets LLM Codegen for API Client SDKs +# Deterministic codegen meets llm codegen for client sdks Traditional codegen is fast and reliable but rigid - change your API spec and you get predictable, identical output every time. LLM codegen is adaptive and intelligent but inconsistent - ask it to generate the same SDK twice and you'll get different results. @@ -6,15 +6,15 @@ This new hybrid approach gives you both: the reliability of deterministic genera ## How it works -Don't care how it works? Skip to: [get started](#get-started-with-your-apis) +Don't care how it works? Skip to [get started](#get-started-with-your-apis). The system runs deterministic codegen to establish the SDK structure. Then LLMs enhance specific components where adaptability adds value - like generating contextual examples, or adding functions that chain together multiple API calls. -The LLM can edit most the files (see [python rules](./assets/python/CLAUDE.md) and [typescript rules](./assets/typescript/CLAUDE.md)) generated by deterministic codegen, and changes will persist across regenerations. The system uses structured pattern matching queries—essentially SQL for source code syntax trees—to precisely target only the elements that need updating. Instead of overwriting entire files, it identifies specific patterns (function signatures, import statements, etc.) and surgically modifies those components while preserving all custom code around them. +The LLM can edit most the files (see [python rules](./assets/python/CLAUDE.md) and [typescript rules](./assets/typescript/CLAUDE.md)) generated by deterministic codegen, and changes will persist across regenerations. The system uses structured pattern matching queries—essentially SQL for source code syntax trees—to precisely target only the elements that need updating. Instead of overwriting entire files, it identifies specific patterns (function signatures, import statements, etc.) and surgically modifies those components while preserving all custom code around them. ![Codegen Process](./assets/codegen-diagram.svg) -Questions? Join our new [community slack](https://join.slack.com/t/sideko-community/shared_invite/zt-3bx0d66ra-iUN69c6qwcd2rnQ5BCY7zQ). +Questions? Join our new [slack](https://join.slack.com/t/sideko-community/shared_invite/zt-3bx0d66ra-iUN69c6qwcd2rnQ5BCY7zQ). The LLM component operates through rules files (like CLAUDE.md) that define what the AI can modify, and coding standards to follow. SDK builders can open the SDKs in Cursor, Claude Code, Gemini, or GH Copilot, and the LLM will follow the guidelines to enhance the code. @@ -34,9 +34,15 @@ The LLM component operates through rules files (like CLAUDE.md) that define what (*NOTE: use this example OpenAPI if you do not have one: [Flights API](./assets/kong-air-flights.yaml)*) +### Git init +Make your first commit before enhancing with the LLM so you can easily review it's work. + +`git init && git add . && git commit -m 'deterministic commit'` + + ### Start prompting -Sample prompt for the example api: +You might have a workflow in mind (make a function that calls this endpoint and then calls that endpoint). See sample prompt for the example api for inspiration: ``` create a flight tracking workflow 1. get all flights @@ -44,13 +50,19 @@ create a flight tracking workflow 3. return "enjoy :)" if the flight has in-flight entertainment and ":(" if not ``` -### Push your changes -Once all tests are passing, push your code to github +### Test and push +Once all tests are passing (the LLM should test itself against the mock server), push your code to github + +### Set up auto openapi-to-sdk sync +(LINK to new docs) -### Set up automatic openapi-to-sdk syncing -- (LINK to new docs) +### Open source +The core network request libraries (and single dependency in each sdk) are public and open source: +- [javascript](https://github.com/Sideko-Inc/make-request-js) +- [python](https://github.com/Sideko-Inc/make-request-py) +- all other supported sdk languages open sourcing soon ### What's next - [join our new slack](https://join.slack.com/t/sideko-community/shared_invite/zt-3bx0d66ra-iUN69c6qwcd2rnQ5BCY7zQ) for questions + sharing your work - we will be adding llm enhancement support to rust, go, java, and c# in the coming weeks -- explore other sideko features like [beautiful api docs](https://docs.sideko.dev/building-documentation/getting-started) +- explore other sideko features like [pretty api docs](https://docs.sideko.dev/building-documentation/getting-started) From acace46d1b08904280961bb1db53d4d91854d324 Mon Sep 17 00:00:00 2001 From: Patrick Kelly Date: Mon, 25 Aug 2025 18:32:28 -0400 Subject: [PATCH 6/6] add mock server screenshot --- releases/determinism-plus-llms/README.md | 2 ++ releases/determinism-plus-llms/assets/test.png | Bin 0 -> 32582 bytes 2 files changed, 2 insertions(+) create mode 100644 releases/determinism-plus-llms/assets/test.png diff --git a/releases/determinism-plus-llms/README.md b/releases/determinism-plus-llms/README.md index de0570c..900adc7 100644 --- a/releases/determinism-plus-llms/README.md +++ b/releases/determinism-plus-llms/README.md @@ -53,6 +53,8 @@ create a flight tracking workflow ### Test and push Once all tests are passing (the LLM should test itself against the mock server), push your code to github +![Tests](./assets/test.png) + ### Set up auto openapi-to-sdk sync (LINK to new docs) diff --git a/releases/determinism-plus-llms/assets/test.png b/releases/determinism-plus-llms/assets/test.png new file mode 100644 index 0000000000000000000000000000000000000000..1f47e7feb987447786311f2a2204bdd57daa9ebe GIT binary patch literal 32582 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 Yhk17rn`_zs`*-P*qOu~DLi&FH2b9z-b^rhX literal 0 HcmV?d00001