diff --git a/examples/images/google.py b/examples/images/google.py new file mode 100644 index 0000000..202b5e4 --- /dev/null +++ b/examples/images/google.py @@ -0,0 +1,41 @@ +import json +import os +import urllib.request + +from dotenv import load_dotenv + +from langbase import Langbase + +load_dotenv() + + +def main(): + # Check if API keys are available + langbase_api_key = os.getenv("LANGBASE_API_KEY") + google_api_key = os.getenv("GOOGLE_API_KEY") + + if not langbase_api_key: + print("❌ Missing LANGBASE_API_KEY in environment variables.") + exit(1) + + if not google_api_key: + print("❌ Missing GOOGLE_API_KEY in environment variables.") + exit(1) + + langbase = Langbase(api_key=langbase_api_key) + + result = langbase.images.generate( + prompt="A futuristic cityscape with flying cars and neon lights, cyberpunk style, highly detailed, 8k resolution", + model="google:gemini-2.5-flash-image-preview", + api_key=google_api_key, + ) + + # Extract image URL and download + image_url = result["choices"][0]["message"]["images"][0]["image_url"]["url"] + output_path = "generated_image_google.png" + urllib.request.urlretrieve(image_url, output_path) + print(f"Image generated and saved to: {output_path}") + + +if __name__ == "__main__": + main() diff --git a/examples/images/openai.py b/examples/images/openai.py new file mode 100644 index 0000000..72b8ce9 --- /dev/null +++ b/examples/images/openai.py @@ -0,0 +1,40 @@ +import os +import urllib.request + +from dotenv import load_dotenv + +from langbase import Langbase + +load_dotenv() + + +def main(): + # Check if API keys are available + langbase_api_key = os.getenv("LANGBASE_API_KEY") + openai_api_key = os.getenv("OPENAI_API_KEY") + + if not langbase_api_key: + print("❌ Missing LANGBASE_API_KEY in environment variables.") + exit(1) + + if not openai_api_key: + print("❌ Missing OPENAI_API_KEY in environment variables.") + exit(1) + + langbase = Langbase(api_key=langbase_api_key) + + result = langbase.images.generate( + prompt="A futuristic cityscape with flying cars and neon lights, cyberpunk style, highly detailed, 8k resolution", + model="openai:gpt-image-1", + api_key=openai_api_key, + ) + + # Extract image URL and download + image_url = result["choices"][0]["message"]["images"][0]["image_url"]["url"] + output_path = "generated_image_openai.png" + urllib.request.urlretrieve(image_url, output_path) + print(f"Image generated and saved to: {output_path}") + + +if __name__ == "__main__": + main() diff --git a/examples/images/openrouter.py b/examples/images/openrouter.py new file mode 100644 index 0000000..867ca8e --- /dev/null +++ b/examples/images/openrouter.py @@ -0,0 +1,40 @@ +import os +import urllib.request + +from dotenv import load_dotenv + +from langbase import Langbase + +load_dotenv() + + +def main(): + # Check if API keys are available + langbase_api_key = os.getenv("LANGBASE_API_KEY") + openrouter_api_key = os.getenv("OPENROUTER_API_KEY") + + if not langbase_api_key: + print("❌ Missing LANGBASE_API_KEY in environment variables.") + exit(1) + + if not openrouter_api_key: + print("❌ Missing OPENROUTER_API_KEY in environment variables.") + exit(1) + + langbase = Langbase(api_key=langbase_api_key) + + result = langbase.images.generate( + prompt="A majestic dragon soaring through clouds above a fantasy castle, epic fantasy art style, detailed scales and wings", + model="openrouter:google/gemini-2.5-flash-image-preview", + api_key=openrouter_api_key, + ) + + # Extract image URL and download + image_url = result["choices"][0]["message"]["images"][0]["image_url"]["url"] + output_path = "generated_image_openrouter.png" + urllib.request.urlretrieve(image_url, output_path) + print(f"Image generated and saved to: {output_path}") + + +if __name__ == "__main__": + main() diff --git a/examples/images/together.py b/examples/images/together.py new file mode 100644 index 0000000..9c7a29a --- /dev/null +++ b/examples/images/together.py @@ -0,0 +1,48 @@ +import os + +import requests +from dotenv import load_dotenv + +from langbase import Langbase + +load_dotenv() + + +def main(): + # Check if API keys are available + langbase_api_key = os.getenv("LANGBASE_API_KEY") + together_api_key = os.getenv("TOGETHER_API_KEY") + + if not langbase_api_key: + print("❌ Missing LANGBASE_API_KEY in environment variables.") + exit(1) + + if not together_api_key: + print("❌ Missing TOGETHER_API_KEY in environment variables.") + exit(1) + + langbase = Langbase(api_key=langbase_api_key) + + result = langbase.images.generate( + prompt="A serene mountain landscape at sunset with a crystal clear lake reflecting the sky", + model="together:black-forest-labs/FLUX.1-schnell-Free", + width=1024, + height=1024, + n=1, + api_key=together_api_key, + ) + + # Extract image URL and download + image_url = result["choices"][0]["message"]["images"][0]["image_url"]["url"] + output_path = "generated_image_together.png" + response = requests.get(image_url, headers={"User-Agent": "Mozilla/5.0"}) + response.raise_for_status() + + with open(output_path, "wb") as f: + f.write(response.content) + + print(f"Image generated and saved to: {output_path}") + + +if __name__ == "__main__": + main() diff --git a/langbase/constants.py b/langbase/constants.py index 223f4b2..b512df9 100644 --- a/langbase/constants.py +++ b/langbase/constants.py @@ -53,3 +53,4 @@ CHUNKER_ENDPOINT = "/v1/chunker" PARSER_ENDPOINT = "/v1/parser" AGENT_RUN_ENDPOINT = "/v1/agent/run" +IMAGES_ENDPOINT = "/v1/images" diff --git a/langbase/langbase.py b/langbase/langbase.py index 3335ff8..066e532 100644 --- a/langbase/langbase.py +++ b/langbase/langbase.py @@ -10,6 +10,7 @@ from .primitives.agent import Agent from .primitives.chunker import Chunker from .primitives.embed import Embed +from .primitives.images import Images from .primitives.memories import Memories from .primitives.parser import Parser from .primitives.pipes import Pipes @@ -43,6 +44,7 @@ def __init__(self, api_key: str = "", base_url: str = "https://api.langbase.com" self.agent = Agent(self) self.chunker_client = Chunker(self) self.embed_client = Embed(self) + self.images = Images(self) self.memories = Memories(self) self.parser_client = Parser(self) self.pipes = Pipes(self) diff --git a/langbase/primitives/images.py b/langbase/primitives/images.py new file mode 100644 index 0000000..7e67f9f --- /dev/null +++ b/langbase/primitives/images.py @@ -0,0 +1,95 @@ +""" +Images API client for the Langbase SDK. +""" + +from typing import Any, Dict, Optional + +from langbase.constants import IMAGES_ENDPOINT +from langbase.request import Request +from langbase.types import ImageGenerationResponse + + +class Images: + """ + Client for image generation operations. + + This class provides methods for generating images using various AI providers. + """ + + def __init__(self, parent): + """ + Initialize the Images client. + + Args: + parent: The parent Langbase instance + """ + self.parent = parent + self.request: Request = parent.request + + def generate( + self, + prompt: str, + model: str, + api_key: str, + width: Optional[int] = None, + height: Optional[int] = None, + image_url: Optional[str] = None, + steps: Optional[int] = None, + n: Optional[int] = None, + negative_prompt: Optional[str] = None, + **kwargs: Any, + ) -> ImageGenerationResponse: + """ + Generate images using various AI providers. + + Args: + prompt: The text prompt to generate images from + model: The model to use for image generation + api_key: The API key for the image generation provider + width: Optional width of the generated image + height: Optional height of the generated image + image_url: Optional URL of an image for image-to-image generation + steps: Optional number of generation steps + n: Optional number of images to generate + negative_prompt: Optional negative prompt to avoid certain elements + **kwargs: Additional parameters for image generation + + Returns: + ImageGenerationResponse containing generated images + + Raises: + ValueError: If required options are missing + Exception: If the API request fails + """ + # Comprehensive input validation + if not prompt: + raise ValueError("Image generation prompt is required.") + + if not model: + raise ValueError("Image generation model is required.") + + if not api_key: + raise ValueError("Image generation API key is required.") + + # Build image parameters + image_params: Dict[str, Any] = { + "prompt": prompt, + "model": model, + "width": width, + "height": height, + "image_url": image_url, + "steps": steps, + "n": n, + "negative_prompt": negative_prompt, + **kwargs, + } + + # Filter out None values + image_params = {k: v for k, v in image_params.items() if v is not None} + + try: + return self.request.post( + IMAGES_ENDPOINT, image_params, headers={"LB-LLM-Key": api_key} + ) + except Exception as error: + raise Exception(f"Image generation failed: {str(error)}") diff --git a/langbase/types.py b/langbase/types.py index c98f6de..db5977a 100644 --- a/langbase/types.py +++ b/langbase/types.py @@ -9,8 +9,6 @@ from typing_extensions import Literal, TypedDict -# NotRequired removed - using Optional instead - # Base types and constants GENERATION_ENDPOINTS = [ "/v1/pipes/run", @@ -774,3 +772,36 @@ class AgentRunOptionsStreamT(TypedDict): # Agent response type (reuses RunResponse) AgentRunResponse = RunResponse + + +# Image generation types +class ImageChoice(TypedDict): + """Image generation choice structure.""" + + logprobs: None + finish_reason: str + native_finish_reason: str + index: int + message: Dict[str, Any] + + +class ImageUsage(TypedDict): + """Image generation usage information.""" + + prompt_tokens: int + completion_tokens: int + total_tokens: int + prompt_tokens_details: Dict[str, int] + completion_tokens_details: Dict[str, int] + + +class ImageGenerationResponse(TypedDict): + """Response from image generation.""" + + id: str + provider: str + model: str + object: str + created: int + choices: List[ImageChoice] + usage: ImageUsage