{"_id":"@amber-core/sdk-mobile","_rev":"3-ab8e731d41f96f3fd971176847def432","name":"@amber-core/sdk-mobile","dist-tags":{"latest":"0.2.1"},"versions":{"0.1.0":{"name":"@amber-core/sdk-mobile","version":"0.1.0","keywords":["amber","platform","llm","react-native","expo","sdk"],"license":"MIT","_id":"@amber-core/sdk-mobile@0.1.0","maintainers":[{"name":"amber-core","email":"cto@ambersoft.llc"}],"dist":{"shasum":"78887550ba22e1255bc2df99aadaeb090951b3cd","tarball":"https://registry.npmjs.org/@amber-core/sdk-mobile/-/sdk-mobile-0.1.0.tgz","fileCount":9,"integrity":"sha512-hNwHaViTqVd3QErfHDcnBb7JWv9a5q/SIQpdnWAh8XM0iWqD91fDNwliP8uuEThmgg00TB94f1XgqNe+JERAuQ==","signatures":[{"sig":"MEQCH3mR0SQaH/h9Gx6zFneD+RhCV+GAUZ+knRnFyVkDyOwCIQDeo5pPgE8gxyWbooRL4MYDLGrtyDYTgs7axJ8mdRQs1g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":45760},"main":"./dist/index.cjs","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=20.19.4"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"}},"gitHead":"191dd86245be337df8914b056ea039f261a236cd","scripts":{"build":"tsup","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm run build"},"_npmUser":{"name":"amber-core","email":"cto@ambersoft.llc"},"_npmVersion":"11.9.0","description":"Mobile SDK for the Amber platform — typed HTTPS client for LLM proxy, config, streaming.","directories":{},"_nodeVersion":"24.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","react":"^19.0.0","typescript":"~5.9.2","@types/react":"^19.2.14"},"peerDependencies":{"react":">=18","react-native":">=0.74"},"peerDependenciesMeta":{"react":{"optional":true},"react-native":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/sdk-mobile_0.1.0_1777465917407_0.04323905520330773","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@amber-core/sdk-mobile","version":"0.2.0","keywords":["amber","platform","llm","react-native","expo","sdk","tools","function-calling","structured-output","vision","images"],"license":"MIT","_id":"@amber-core/sdk-mobile@0.2.0","maintainers":[{"name":"amber-core","email":"cto@ambersoft.llc"}],"dist":{"shasum":"6ad933779ada8432db1b7599645b6587de84a815","tarball":"https://registry.npmjs.org/@amber-core/sdk-mobile/-/sdk-mobile-0.2.0.tgz","fileCount":17,"integrity":"sha512-Q/W4v89t+/TA7nkjUqBdB0ug/xJMWVmjY80Iao7FUYnAT+DrQ/z8tRnqRLmtkhbY7H9bRRK8EmmqYh0HkZmUaQ==","signatures":[{"sig":"MEYCIQCpNEb7HT/dccoJw1MS9G/GqauxElCoxr0QUufL6cLbNgIhAPJ57bobfW6f+ChzKwT7yTI5kqTmsM97SF/6aVHov20b","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":80777},"main":"./dist/index.cjs","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=20.19.4"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"},"./zod":{"types":"./dist/zod.d.ts","import":"./dist/zod.mjs","require":"./dist/zod.cjs"}},"gitHead":"47dd7dfd1317b18ae0659893f654f7a6d58e1ac6","scripts":{"build":"tsup","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm run build"},"_npmUser":{"name":"amber-core","email":"cto@ambersoft.llc"},"_npmVersion":"11.9.0","description":"Mobile SDK for the Amber platform — typed HTTPS client for LLM proxy, config, streaming, tools, structured output, images.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"zod":"^4.3.6","tsup":"^8.3.5","react":"^19.0.0","typescript":"~5.9.2","@types/react":"^19.2.14"},"peerDependencies":{"zod":"^4","react":">=18","react-native":">=0.74"},"peerDependenciesMeta":{"zod":{"optional":true},"react":{"optional":true},"react-native":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/sdk-mobile_0.2.0_1777534233428_0.16112016800472095","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@amber-core/sdk-mobile","version":"0.2.1","description":"Mobile SDK for the Amber platform — typed HTTPS client for LLM proxy, config, streaming, tools, structured output, images.","license":"MIT","main":"./dist/index.cjs","module":"./dist/index.mjs","types":"./dist/index.d.ts","sideEffects":false,"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"},"./zod":{"types":"./dist/zod.d.ts","import":"./dist/zod.mjs","require":"./dist/zod.cjs"}},"scripts":{"build":"tsup","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm run build"},"peerDependencies":{"react":">=18","react-native":">=0.74","zod":"^4"},"peerDependenciesMeta":{"react":{"optional":true},"react-native":{"optional":true},"zod":{"optional":true}},"devDependencies":{"@types/react":"^19.2.14","react":"^19.0.0","tsup":"^8.3.5","typescript":"~5.9.2","zod":"^4.3.6"},"publishConfig":{"access":"public"},"keywords":["amber","platform","llm","react-native","expo","sdk","tools","function-calling","structured-output","vision","images"],"engines":{"node":">=20.19.4"},"gitHead":"b6acfb6a25833ca3ebaad218db94f667b5226b19","_id":"@amber-core/sdk-mobile@0.2.1","_nodeVersion":"24.14.0","_npmVersion":"11.9.0","dist":{"integrity":"sha512-8pt2Ws5BBvO09cszhlJ1cTEeQ2BEVNS/pmG0e1ICDxG1HxXPvi/oCOBujZvz0V5s8sZgYDR95NPfXx4MB+tiJA==","shasum":"945d5e9c81010731de7f3a7209b22ec0493be339","tarball":"https://registry.npmjs.org/@amber-core/sdk-mobile/-/sdk-mobile-0.2.1.tgz","fileCount":17,"unpackedSize":84484,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEMCH1Dw6LEImf2rzRuJafpzBhZ9Xm54B+j3OKzn/IB73IoCIGWPGD5MBibH3Spov9GPkLqhh8b43UIuJyPMfSKmjICu"}]},"_npmUser":{"name":"amber-core","email":"cto@ambersoft.llc"},"directories":{},"maintainers":[{"name":"amber-core","email":"cto@ambersoft.llc"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sdk-mobile_0.2.1_1777538358358_0.6968947008465243"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-29T12:31:57.323Z","modified":"2026-04-30T08:39:18.648Z","0.1.0":"2026-04-29T12:31:57.543Z","0.2.0":"2026-04-30T07:30:33.639Z","0.2.1":"2026-04-30T08:39:18.563Z"},"license":"MIT","keywords":["amber","platform","llm","react-native","expo","sdk","tools","function-calling","structured-output","vision","images"],"description":"Mobile SDK for the Amber platform — typed HTTPS client for LLM proxy, config, streaming, tools, structured output, images.","maintainers":[{"name":"amber-core","email":"cto@ambersoft.llc"}],"readme":"# @amber-core/sdk-mobile\n\nTyped HTTPS client for the Amber shared platform. Use it from React Native / Expo apps to call the LLM proxy without putting provider keys in the bundle.\n\n**v0.2.1** adds `llm.images.edit` for prompt-driven editing of an existing image. **v0.2.0** added tool calling, structured output (`llm.object`), image input (vision), and image generation (`llm.images.generate`).\n\n## Install\n\n```sh\nnpm install @amber-core/sdk-mobile\n```\n\n## Setup\n\n```ts\nimport { createPlatformClient } from \"@amber-core/sdk-mobile\";\n\nconst platform = createPlatformClient({\n  apiUrl: \"https://abc.execute-api.us-east-1.amazonaws.com\",\n  llmStreamUrl: \"https://xyz.lambda-url.us-east-1.on.aws/\",\n  appId: process.env.EXPO_PUBLIC_PLATFORM_APP_ID!,\n  getToken: () => clerk.getToken({ template: \"platform\" }),\n});\n```\n\n## Chat\n\n```ts\nconst reply = await platform.llm.chat({\n  model: \"gpt-4o-mini\",\n  messages: [{ role: \"user\", content: \"hi\" }],\n  estimatedTokens: 100,\n});\n\nfor await (const chunk of platform.llm.chatStream({\n  model: \"gpt-4o-mini\",\n  messages: [{ role: \"user\", content: \"tell me a story\" }],\n  estimatedTokens: 500,\n})) {\n  if (chunk.type === \"delta\") process.stdout.write(chunk.content);\n}\n```\n\n## Tools (low-level)\n\n```ts\nconst tools = [\n  {\n    name: \"get_weather\",\n    description: \"Get current weather for a city.\",\n    inputSchema: {\n      type: \"object\",\n      properties: { city: { type: \"string\" } },\n      required: [\"city\"],\n    },\n  },\n];\n\nconst r1 = await platform.llm.chat({\n  model: \"claude-3-5-sonnet-20241022\",\n  messages: [{ role: \"user\", content: \"Weather in Berlin?\" }],\n  tools,\n  estimatedTokens: 400,\n});\n\nif (r1.finishReason === \"tool_use\") {\n  const toolUses = r1.blocks.filter((b) => b.type === \"tool_use\");\n  // Execute tools, then send results back as a new turn:\n  const r2 = await platform.llm.chat({\n    model: \"claude-3-5-sonnet-20241022\",\n    tools,\n    messages: [\n      { role: \"user\", content: \"Weather in Berlin?\" },\n      { role: \"assistant\", content: r1.blocks },\n      {\n        role: \"user\",\n        content: toolUses.map((tu) => ({\n          type: \"tool_result\",\n          toolUseId: tu.id,\n          content: \"18°C, cloudy\",\n        })),\n      },\n    ],\n    estimatedTokens: 500,\n  });\n  console.log(r2.content);\n}\n```\n\nTool streaming emits `tool_use_start` → one or more `tool_use_delta` (raw JSON fragments) → `tool_use_stop` (with parsed `input`):\n\n```ts\nfor await (const c of platform.llm.chatStream({ tools, messages, model, estimatedTokens: 500 })) {\n  if (c.type === \"delta\") process.stdout.write(c.content);\n  else if (c.type === \"tool_use_start\") console.log(\"\\n→ calling\", c.name);\n  else if (c.type === \"tool_use_stop\") console.log(\"input:\", c.input);\n}\n```\n\n## Structured output (`llm.object`)\n\nPass a JSON Schema and get back a typed object. The backend forces JSON output with both providers (OpenAI native `response_format`, Anthropic via forced tool — transparent to you).\n\n```ts\nconst { object } = await platform.llm.object<{\n  city: string;\n  forecast: { day: string; tempC: number }[];\n}>({\n  model: \"gpt-4o-mini\",\n  messages: [{ role: \"user\", content: \"5-day forecast for Berlin in JSON.\" }],\n  schemaName: \"Forecast\",\n  schema: {\n    type: \"object\",\n    properties: {\n      city: { type: \"string\" },\n      forecast: {\n        type: \"array\",\n        items: {\n          type: \"object\",\n          properties: { day: { type: \"string\" }, tempC: { type: \"number\" } },\n          required: [\"day\", \"tempC\"],\n        },\n      },\n    },\n    required: [\"city\", \"forecast\"],\n  },\n  estimatedTokens: 600,\n});\n```\n\n### With Zod (optional)\n\nThe `/zod` subpath converts a Zod 4 schema into JSON Schema. Zod is a peer dep — only loaded if you import this subpath.\n\n```ts\nimport { z } from \"zod\";\nimport { fromZod } from \"@amber-core/sdk-mobile/zod\";\n\nconst Forecast = z.object({\n  city: z.string(),\n  forecast: z.array(z.object({ day: z.string(), tempC: z.number() })),\n});\n\nconst { object } = await platform.llm.object<z.infer<typeof Forecast>>({\n  model: \"gpt-4o-mini\",\n  messages: [{ role: \"user\", content: \"5-day forecast for Berlin.\" }],\n  schema: fromZod(Forecast),\n  schemaName: \"Forecast\",\n  estimatedTokens: 600,\n});\n```\n\n## Vision (image input)\n\nSend images as content blocks. Both URL and base64 sources work with OpenAI; Anthropic accepts base64 only.\n\n```ts\nimport { estimateImageTokens } from \"@amber-core/sdk-mobile\";\n\nconst image = {\n  type: \"image\" as const,\n  source: { type: \"url\" as const, url: \"https://example.com/cat.jpg\" },\n};\n\nawait platform.llm.chat({\n  model: \"gpt-4o\",\n  messages: [\n    {\n      role: \"user\",\n      content: [\n        image,\n        { type: \"text\", text: \"What breed?\" },\n      ],\n    },\n  ],\n  estimatedTokens: 400 + estimateImageTokens(image, \"openai\", { width: 1024, height: 768 }),\n});\n```\n\n## Image generation\n\n```ts\nconst res = await platform.llm.images.generate({\n  model: \"dall-e-3\",\n  prompt: \"An isometric studio for a small mobile team\",\n  size: \"1024x1024\",\n  responseFormat: \"url\",\n  estimatedCredits: 1,\n});\n\nfor (const img of res.images) console.log(img.url);\n```\n\n> **Mobile tip:** prefer `responseFormat: \"url\"`. Base64 payloads can be megabytes and saturate the React Native bridge.\n\nImage generation runs on its own daily credit bucket — exhausting chat tokens does not block image calls and vice versa. Only OpenAI-backed apps can call `images.generate`; Anthropic-backed apps return `400 unsupported_capability`.\n\n## Image editing\n\nTransform an existing image with a prompt — typical for portrait restyling, costume swaps, etc. Source image goes as base64 inside the JSON body (the platform's HTTP layer is JSON-only). On RN read the file with `expo-file-system`'s `readAsStringAsync(uri, { encoding: 'base64' })`.\n\n```ts\nimport * as FileSystem from \"expo-file-system\";\n\nconst base64 = await FileSystem.readAsStringAsync(playerPhotoUri, {\n  encoding: FileSystem.EncodingType.Base64,\n});\n\nconst res = await platform.llm.images.edit({\n  model: \"gpt-image-1\",\n  prompt: \"Render this person as a high-fantasy character portrait, watercolor style\",\n  image: base64,\n  imageMediaType: \"image/jpeg\",\n  size: \"1024x1024\",\n  responseFormat: \"url\",\n  estimatedCredits: 1,\n});\n\nconsole.log(res.images[0].url);\n```\n\n> **Body limit:** Lambda HTTP API caps body at ~6 MB after base64 — keep raw image ≲ 4.5 MB. Downscale phone photos before encoding.\n\nOptional `mask` (base64 RGBA PNG, transparent pixels = the area to repaint). Same provider rules as `generate`: OpenAI only, separate credit bucket.\n\n## API\n\n- `createPlatformClient(opts) → PlatformClient`\n- `platform.config.get() → AppConfigResponse`\n- `platform.llm.chat(req) → LlmChatResponse`\n- `platform.llm.chatStream(req, signal?) → AsyncGenerator<LlmStreamChunk>`\n- `platform.llm.object<T>(req) → LlmObjectResponse<T>`\n- `platform.llm.images.generate(req) → ImagesGenerateResponse`\n- `platform.llm.images.edit(req) → ImagesEditResponse`\n- `estimateImageTokens(image, provider, dimensions?) → number`\n- `fromZod(schema)` from `@amber-core/sdk-mobile/zod`\n\nThe client injects `Authorization: Bearer <token>` and `X-App-Id` headers automatically. For non-streaming `chat`, `object`, `images.generate`, and `images.edit`, an `X-Idempotency-Key` is generated per call to make retries safe.\n\n## Migration from 0.1.0\n\n`LlmMessage` now accepts content blocks alongside plain strings — old code (`{ role: \"user\", content: \"...\" }`) keeps working without changes. The streaming SSE `delta` event now carries multiple payload shapes discriminated by `type` (`\"delta\"` for text, `\"tool_use_*\"` for tool calls); existing code that reads `chunk.content` for `chunk.type === \"delta\"` is unchanged.\n","readmeFilename":"README.md"}