{"id":40610904,"url":"https://github.com/hack-dance/aisx","last_synced_at":"2026-01-21T05:10:34.061Z","repository":{"id":290866742,"uuid":"955509291","full_name":"hack-dance/aisx","owner":"hack-dance","description":"aisx is a JSX-based templating engine for generating strings with TypeScript support. It provides a React-like syntax for creating, composing, and managing complex LLM prompts and other structured text templates.","archived":false,"fork":false,"pushed_at":"2025-06-12T16:17:13.000Z","size":190,"stargazers_count":2,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-25T09:41:49.721Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hack-dance.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-03-26T18:47:00.000Z","updated_at":"2025-07-07T07:15:01.000Z","dependencies_parsed_at":"2025-05-01T01:49:47.111Z","dependency_job_id":null,"html_url":"https://github.com/hack-dance/aisx","commit_stats":null,"previous_names":["hack-dance/aisx"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/hack-dance/aisx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hack-dance%2Faisx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hack-dance%2Faisx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hack-dance%2Faisx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hack-dance%2Faisx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hack-dance","download_url":"https://codeload.github.com/hack-dance/aisx/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hack-dance%2Faisx/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28627390,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-21T04:47:28.174Z","status":"ssl_error","status_checked_at":"2026-01-21T04:47:22.943Z","response_time":86,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-21T05:10:33.191Z","updated_at":"2026-01-21T05:10:34.050Z","avatar_url":"https://github.com/hack-dance.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://public.assets.tube/aisx.svg\" style=\"max-width:400px;\" /\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n_aisx - AI Markup Language_\n\u003c/p\u003e\n\n---\n\naisx is a JSX-based templating engine for generating strings with TypeScript support. It provides a React-like syntax for creating, composing, and managing complex LLM prompts and other structured text templates.\n\n\u003e From the makers of [instructor-js](https://github.com/hack-dance/instructor-js), [zod-stream](https://github.com/hack-dance/zod-stream), and [schema-stream](https://github.com/hack-dance/schema-stream) - While our other tools focus on structured *outputs*, aisx is all about structured *inputs*. We've come full circle! 🔄\n\n## Why aisx?\n\nManaging complex prompts for LLMs using template strings or manually concatenated functions quickly becomes a nightmare. aisx provides a clean, declarative way to define structured inputs for LLMs, making your prompts:\n\n- Composable and reusable with component-based architecture\n- Type-safe with full TypeScript support\n- Easily and independently testable\n- Dynamic with support for conditional rendering and variable inputs\n- Maintainable with a familiar React-like syntax\n\n## Features\n\n- Write JSX/TSX that renders to strings\n- Async components support for dynamic content generation\n- Full TypeScript support out of the box\n- Compatible with both Bun and Node.js\n- Simple API with zero dependencies\n- Supports `.aisx.tsx` file extension\n\n## Installation\n\n```bash\n# Using npm\nnpm install aisx\n\n# Using yarn\nyarn add aisx\n\n# Using pnpm\npnpm add aisx\n\n# Using bun\nbun add aisx\n```\n\n## Setup\n\nFor comprehensive setup instructions and troubleshooting, please refer to [SETUP.md](./SETUP.md).\n\nBelow is a quick overview of how to set up aisx in your project. aisx can be configured in various ways depending on your project requirements:\n\n### Standalone Setup (Recommended)\n\nThis setup is ideal for projects that don't use React or other JSX libraries.\n\n1. **Configure TypeScript**\n\n   Create or update your `tsconfig.json` to extend aisx's configuration:\n\n   ```json\n   {\n     \"extends\": \"aisx/tsconfig/aisx\",\n     \"compilerOptions\": {\n       // Your additional compiler options\n     }\n   }\n   ```\n\n   The aisx tsconfig preset includes:\n   - `jsx: \"preserve\"`\n   - `jsxFactory: \"jsx\"`\n   - `jsxFragmentFactory: \"Fragment\"`\n   - `jsxImportSource: \"aisx\"`\n\n2. **For Bun Users**\n\n   Add the following to your `bunfig.toml`:\n\n   ```toml\n   # JSX settings for aisx files\n   jsxImportSource = \"aisx\"\n\n   # File-specific loaders\n   [loader]\n   \".tsx\" = \"tsx\"\n   \".aisx.tsx\" = \"tsx\"\n   \".aisx.test.tsx\" = \"tsx\"\n   ```\n\n3. **File Naming Convention**\n\n   Name your aisx template files with `.aisx.tsx` extension:\n\n   ```\n   template.aisx.tsx\n   myPrompt.aisx.tsx\n   ```\n\n### Pragma-Based Setup\n\nIf you can't or don't want to configure TypeScript globally, you can use pragma comments at the top of each aisx file:\n\n```tsx\n/** @jsxImportSource aisx */\n\nexport function MyTemplate() {\n  return (\n    \u003cmessage\u003e\n      \u003cgreeting\u003eHello, world!\u003c/greeting\u003e\n    \u003c/message\u003e\n  );\n}\n```\n\n## Usage\n\n### Basic Example\n\n```tsx\n// template.aisx.tsx\n/** @jsxImportSource aisx */\n\nexport function Greeting(props: { name: string }) {\n  return (\n    \u003cgreeting\u003e\n      Hello, {props.name}!\n    \u003c/greeting\u003e\n  );\n}\n\nexport function AIPrompt(props: { instructions: string, role: string }) {\n  return (\n    \u003cmessage\u003e\n      \u003crole\u003e{props.role}\u003c/role\u003e\n      \u003cinstructions\u003e{props.instructions}\u003c/instructions\u003e\n    \u003c/message\u003e\n  );\n}\n```\n\n```ts\n// index.ts\nimport { Greeting, AIPrompt } from './template.aisx';\n\n// There are multiple ways to use aisx components:\n\n// 1. Function call (simplest approach)\nconst greeting = Greeting({ name: \"aisx\" });\nconsole.log(\"Greeting Template:\");\nconsole.log(greeting);\n\n// 2. JSX syntax (requires JSX support in your project)\nconst prompt = \u003cAIPrompt \n  role=\"assistant\" \n  instructions=\"You are a helpful AI assistant.\"\n/\u003e;\nconsole.log(\"AI Prompt Template:\");\nconsole.log(prompt);\n\n// 3. Using the render function (useful for async components)\nimport aisx from 'aisx';\nconst renderedPrompt = aisx.render(\u003cAIPrompt \n  role=\"assistant\" \n  instructions=\"You are a helpful AI assistant.\"\n/\u003e);\n```\n\n### Advanced Features\n\n#### Async Components\n\naisx supports async components, making it possible to dynamically generate content based on external data:\n\n```tsx\n/** @jsxImportSource aisx */\n\n// Async component that fetches data\nexport async function DynamicPrompt(props: { userId: string }) {\n  const userData = await fetchUserData(props.userId);\n  \n  return (\n    \u003cinstruction\u003e\n        You are assisting {userData.name} who is interested in {userData.interests.join(', ')}.\n        Please tailor your responses accordingly.\n    \u003c/instruction\u003e\n  );\n}\n\n// Usage options:\n// 1. Await the JSX expression\nconst dynamicPrompt = await \u003cDynamicPrompt userId=\"123\" /\u003e;\n\n// 2. Use the render function\nconst dynamicPrompt = await aisx.render(\u003cDynamicPrompt userId=\"123\" /\u003e);\n\n// 3. Direct function call (returns a Promise)\nconst dynamicPrompt = await DynamicPrompt({ userId: \"123\" });\n```\n\n#### Real-World Example: Context Builder\n\nHere's how you might use aisx to build a complex context for an LLM call:\n\n```tsx\n/** @jsxImportSource aisx */\n\n// Component to format user profile data\nasync function UserContext({ userId }: { userId: string }) {\n  const user = await fetchUserProfile(userId);\n  const recentOrders = await fetchRecentOrders(userId);\n  \n  return (\n    \u003cuser_context\u003e\n      \u003cprofile\u003e\n        \u003cname\u003e{user.fullName}\u003c/name\u003e\n        \u003caccount_type\u003e{user.accountType}\u003c/account_type\u003e\n        \u003cmembership_since\u003e{user.createdAt}\u003c/membership_since\u003e\n        \u003cpreferences\u003e{JSON.stringify(user.preferences)}\u003c/preferences\u003e\n      \u003c/profile\u003e\n      \n      \u003crecent_orders\u003e\n        {recentOrders.map(order =\u003e (\n          \u003corder id={order.id} date={order.date}\u003e\n            \u003cstatus\u003e{order.status}\u003c/status\u003e\n            \u003citems\u003e{order.items.map(item =\u003e item.name).join(\", \")}\u003c/items\u003e\n          \u003c/order\u003e\n        ))}\n      \u003c/recent_orders\u003e\n    \u003c/user_context\u003e\n  );\n}\n\n// Component to build product recommendations\nasync function ProductRecommendations({ userId, category }: { userId: string; category: string }) {\n  const recommendations = await generateRecommendations(userId, category);\n  \n  return (\n    \u003crecommendations\u003e\n      {recommendations.map(product =\u003e (\n        \u003cproduct id={product.id}\u003e\n          \u003cname\u003e{product.name}\u003c/name\u003e\n          \u003cmatch_score\u003e{product.score}\u003c/match_score\u003e\n          \u003ckey_features\u003e{product.features.join(\", \")}\u003c/key_features\u003e\n        \u003c/product\u003e\n      ))}\n    \u003c/recommendations\u003e\n  );\n}\n\n// Main prompt builder that composes multiple context sources\nexport async function CustomerSupportPrompt({ \n  userId, \n  query,\n  productCategory\n}: { \n  userId: string; \n  query: string;\n  productCategory?: string;\n}) {\n  return (\n    \u003cprompt\u003e\n      \u003csystem\u003e\n        \u003cinstructions\u003e\n          You are a helpful customer support assistant for our e-commerce store.\n          Use the provided context to answer customer queries accurately.\n          If you don't know the answer, admit it and offer to connect them with a human agent.\n        \u003c/instructions\u003e\n      \u003c/system\u003e\n      \n      \u003ccontext\u003e\n        \u003cUserContext userId={userId} /\u003e\n        \n        {productCategory \u0026\u0026 (\n          \u003cProductRecommendations userId={userId} category={productCategory} /\u003e\n        )}\n        \n        \u003ccurrent_query\u003e\n          \u003ctimestamp\u003e{new Date().toISOString()}\u003c/timestamp\u003e\n          \u003cquery_text\u003e{query}\u003c/query_text\u003e\n        \u003c/current_query\u003e\n      \u003c/context\u003e\n    \u003c/prompt\u003e\n  );\n}\n```\n\n#### Composition and Reuse\n\nBuild complex prompts through composition, just like React components:\n\n```tsx\n/** @jsxImportSource aisx */\n\nfunction SystemPrompt(props: { persona: string }) {\n  return (\n    \u003cs\u003e\n      You are {props.persona}. Respond in a way that matches this persona.\n    \u003c/s\u003e\n  );\n}\n\nfunction ContextProvider(props: { \n  data: Record\u003cstring, unknown\u003e,\n  children: React.ReactNode \n}) {\n  return (\n    \u003ccontext\u003e\n      \u003cdata\u003e{JSON.stringify(props.data)}\u003c/data\u003e\n\n      {props.children}\n    \u003c/context\u003e\n  );\n}\n\nexport function ComplexPrompt() {\n  const contextData = {\n    timestamp: Date.now(),\n    version: \"1.0.0\"\n  };\n  \n  return (\n    \u003cmessage\u003e\n      \u003cSystemPrompt persona=\"a helpful programming assistant\" /\u003e\n\n      \u003cContextProvider data={contextData}\u003e\n        \u003cuser_query\u003eHow do I use TypeScript with React?\u003c/user_query\u003e\n      \u003c/ContextProvider\u003e\n    \u003c/message\u003e\n  );\n}\n```\n\n#### Conditional Rendering\n\nImplement dynamic prompts based on conditions:\n\n```tsx\n/** @jsxImportSource aisx */\n\nexport function ConditionalPrompt(props: { \n  skill: \"beginner\" | \"intermediate\" | \"advanced\",\n  topic: string \n}) {\n  return (\n    \u003cprompt\u003e\n      \u003cs\u003eYou are a coding tutor.\u003c/s\u003e\n      \u003cinstructions\u003e\n        {props.skill === \"beginner\" \u0026\u0026 (\n          \u003cbeginner\u003e\n            Explain {props.topic} in simple terms with basic examples.\n          \u003c/beginner\u003e\n        )}\n        \n        {props.skill === \"intermediate\" \u0026\u0026 (\n          \u003cintermediate\u003e\n            Provide detailed explanation of {props.topic} with practical examples.\n          \u003c/intermediate\u003e\n        )}\n        \n        {props.skill === \"advanced\" \u0026\u0026 (\n          \u003cadvanced\u003e\n            Explain advanced concepts of {props.topic} with complex examples and best practices.\n          \u003c/advanced\u003e\n        )}\n      \u003c/instructions\u003e\n    \u003c/prompt\u003e\n  );\n}\n```\n\n## Customizing JSX Elements\n\nJust like with React, you can compose aisx components, execute code, etc.\n\naisx also allows you to define custom JSX elements with TypeScript interfaces. You can extend the JSX namespace in your own declaration files:\n\n```typescript\n// custom.d.ts\ndeclare namespace JSX {\n  interface IntrinsicElements {\n    // Add your custom elements\n    thinking_model: Record\u003cstring, unknown\u003e\n    system_message: {\n      temperature?: number\n      model?: string\n    }\n  }\n}\n```\n\nWith this configuration, you can use your custom elements in aisx templates with built-in type checking:\n\n```tsx\n/** @jsxImportSource aisx */\n\nexport function ThinkingPrompt() {\n  return (\n    \u003cthinking_model\u003e\n      \u003csystem_message temperature={0.7} model=\"gpt-4\"\u003e\n        First, analyze the problem step by step.\n        Then, provide a solution with explanations.\n      \u003c/system_message\u003e\n    \u003c/thinking_model\u003e\n  );\n}\n```\n\n## Testing aisx Components\n\nYou can unit test your aisx components just like you would test React components:\n\n```tsx\n// prompt.test.tsx\nimport { expect, test } from 'bun:test';\nimport { GreetingPrompt } from './prompt.aisx';\n\ntest('GreetingPrompt renders correctly', async () =\u003e {\n  const result = \u003cGreetingPrompt name=\"Tester\" /\u003e;\n  expect(result).toContain('Hello, Tester!');\n  expect(result).toContain('\u003cgreeting\u003e');\n});\n```\n\n## Important Notes\n\n### Integration with React Projects\n\n**Not Recommended**: Running aisx alongside React in the same project can lead to JSX runtime conflicts and type checking issues. It's possible to do in situations where it's really needed, but the recommended approach is to keep your templates in a separate package that doesn't need to transpile React too.\n\nFor projects that require both React UI components and aisx templates, consider one of these approaches:\n\n1. **Separate Packages**: Keep aisx templates in a separate package from your React components.\n\n### Output Formats\n\nWhile the default output is an XML-like format, you can create wrapper components to output in different formats like Markdown or plain text without the element wrappers.\n\n## Development\n\nFor contributors looking to develop aisx further:\n\n1. Clone the repository\n2. Install dependencies with `bun install`\n3. Build the package with `bun run build`\n4. Run tests with `bun test`\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhack-dance%2Faisx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhack-dance%2Faisx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhack-dance%2Faisx/lists"}