# RequirementAIWrapper

---

# Props

| Prop           | Type                                                         |
| -------------- | ------------------------------------------------------------ |
| userStory      | `IUserStory \| null`                                         |
| setUserStory   | `React.Dispatch<React.SetStateAction<IUserStory \| null>>`   |
| aiReasoning    | `TAIReasoning[]`                                             |
| setAiReasoning | `React.Dispatch<React.SetStateAction<TAIReasoning[]>>`       |
| citations      | `UserStoryCitations[]`                                       |
| setCitations   | `React.Dispatch<React.SetStateAction<UserStoryCitations[]>>` |
| children       | `ReactNode`                                                  |

---

# Data Structures

## IUserStory

```ts
export interface IUserStory {
  artifactId: string;
  title: string;
  value: string;
  sub_features?: IUserStory[];

  storyNumber?: string;
  description?: string;
  section?: "intro" | "subfeature";
  blockNumber?: string;

  citation?: UserStoryCitations;
  aiReasoning?: TAIReasoning[];

  isEnd?: boolean;
}
```

---

## TAIReasoning

```ts
export type TAIReasoning = {
  id: string;
  reason: string;
  gap: string;
  relevance_score: number;
  reasonSource?: string;
  gapSource?: string;
  isOpen?: boolean;
  relevance_score_diff?: number;
  explanation?: string;
};
```

---

## UserStoryCitations

```ts
export type UserStoryCitations = {
  image_citations?: ImageCitation[];
  appmod_citations?: TextCitation[];
  audio_citations?: AudioCitation[];
  file_citations?: FileCitation[];
  file_image_data?: ImageCitationsForFile | null;
  rca_citations?: RcaCitation[];
  web_citations?: WebCitation[];
  code_generated?: GeneratedCodeCitation[];
};
```

---

## ImageCitationsForFile

```ts
interface ImageCitationsForFile {
  [key: string]: FileImageData;
}
```

---

## FileImageData

```ts
interface FileImageData {
  filename: string;
  signed_url: string;
  gs_url: string;
}
```

---

# Usage

## Create State

```tsx
const [userStory, setUserStory] = useState<IUserStory | null>(null);

const [aiReasoning, setAiReasoning] = useState<TAIReasoning[]>([]);

const [citations, setCitations] = useState<UserStoryCitations[]>([]);
```

---

## Wrap Components

```tsx
<RequirementAIWrapper
  userStory={userStory}
  setUserStory={setUserStory}
  aiReasoning={aiReasoning}
  setAiReasoning={setAiReasoning}
  citations={citations}
  setCitations={setCitations}
>
  {children}
</RequirementAIWrapper>
```

---

# Context Value

The wrapper provides the following context value:

```ts
{
  (userStory,
    setUserStory,
    aiReasoning,
    setAiReasoning,
    citations,
    setCitations);
}
```

# Usage

```tsx
import { useStreamContentHandler } from "@e-llm-studio/requirement-ai";

function StreamingComponent() {
  const { processStreamContent } = useStreamContentHandler({
    setUserStory,
    setAiReasoning,
    setUserStoryCitations,
    onStreamingCompleted,
    isGraphQLCallInProgress,
    knowledgeBase,
    projectAnalyserSummary,
    dataSourceFiles,
    aiReasoning,
    setRedisValue,
    fetchCitationByBlock,
  });

  const handleStream = (blockList) => {
    processStreamContent(blockList, metadata, "story-artifact-id");
  };

  return <div>Streaming...</div>;
}
```

---

# Hook API

## useStreamContentHandler(props)

### Returns

```ts
{
  processStreamContent: (
    blockList: BlockStreamItem[],
    customMetadata: any,
    storyArtifactID: string
  ) => void
}
```

---

# Props

## setUserStory

React state setter used to store generated user stories.

```ts
React.Dispatch<React.SetStateAction<IUserStory[]>>;
```

---

## setAiReasoning

State setter used to store extracted AI reasoning.

```ts
React.Dispatch<React.SetStateAction<TAIReasoning[]>>;
```

---

## setUserStoryCitations

State setter used to store citations generated from blocks.

```ts
React.Dispatch<React.SetStateAction<UserStoryCitations>>;
```

---

## onStreamingCompleted

Callback executed once **all citation generation tasks have completed**.

```ts
(gap_items_prompt?: string, risk_items_prompt?: string) => Promise<void>;
```

---

## isGraphQLCallInProgress

Boolean flag indicating whether a GraphQL call is currently running.

This prevents the completion callback from firing prematurely.

```ts
boolean;
```

---

## knowledgeBase

Knowledge base metadata associated with the current stream.

```ts
any;
```

---

## projectAnalyserSummary

Project analysis summary used during processing.

```ts
any;
```

---

## dataSourceFiles

Information about uploaded data source files.

```ts
{
  document: string[];
  image: string[];
  meetingRecording: string[];
}
```

---

## aiReasoning

Existing AI reasoning state.

```ts
TAIReasoning[]
```

---

## setRedisValue

Function responsible for storing streaming state in Redis.

```ts
<T>({ key, value }: SetRedisParams<T>) => Promise<any>;
```

Example implementation:

```ts
async function setRedisValue({ key, value }) {
  await redis.set(key, JSON.stringify(value));
}
```

---

## fetchCitationByBlock

Async function responsible for generating citations for a given block.

```ts
(block: BlockStreamItem, metadata: any, ai_reasonings: TAIReasoning[]) =>
  Promise<IGetCitationsByBlock>;
```

---

# Types

## BlockStreamItem

Represents a streamed AI block.

```ts
type BlockStreamItem = {
  title: string;
  section: "intro" | "subfeature";
  description?: string;
  blockNumber: string;
  value: string;
  storyNumber: string;
};
```

---

# Streaming Flow

```
AI Stream
   ↓
Block List
   ↓
processStreamContent
   ↓
Reasoning Extraction
   ↓
User Story Generation
   ↓
Citation Generation
   ↓
Redis Persistence
   ↓
Streaming Completed Callback
```

---

# Internal Safeguards

The hook includes several mechanisms to optimize streaming performance:

| Mechanism                          | Purpose                                   |
| ---------------------------------- | ----------------------------------------- |
| updateCitationCallKeysRef          | Prevents duplicate citation requests      |
| citationGenerationCompletedKeysRef | Tracks completed citation tasks           |
| lastProcessedBlockListHashRef      | Avoids reprocessing identical block lists |
| lastRedisValueRef                  | Prevents redundant Redis writes           |

---

# Best Practices

- Ensure `fetchCitationByBlock` is idempotent.
- Avoid extremely frequent calls to `processStreamContent` without batching.
- Always serialize Redis values using JSON.

---

# UserStoryScreen Component

A composable UI component system for displaying and editing user stories with AI reasoning citations. It must be wrapped within `RequirementAIWrapper` to access shared state.

## Quick Demo

```tsx
import React, { useState } from "react";
import RequirementAIWrapper from "@e-llm-studio/requirement-ai";
import UserStoryScreen from "@e-llm-studio/requirement-ai";
import UserStoryWithCitations from "@e-llm-studio/requirement-ai";
import RichTextEditor from "@e-llm-studio/requirement-ai";
import AtomicButton from "@e-llm-studio/requirement-ai";

function MyStoryApp() {
  const [userStory, setUserStory] = useState<IUserStory[]>([
    {
      artifactId: "story-1",
      title: "User Login",
      value: "As a user, I want to login to the system",
    },
  ]);

  const [aiReasoning, setAiReasoning] = useState<TAIReasoning[]>([]);
  const [citations, setCitations] = useState<UserStoryCitations[]>([]);

  return (
    <RequirementAIWrapper
      userStory={userStory}
      setUserStory={setUserStory}
      aiReasoning={aiReasoning}
      setAiReasoning={setAiReasoning}
      citations={citations}
      setCitations={setCitations}
      apiCachingConfig={{
        queryClient: null, // replace with original tanstack-query client
        useGetSignedUrlQuery: (gsUtilPath: string, cacheKey: string[]) => null, // replace with original hook
        useGetSignedUrlMutation: (gsUtilPath: string) => null, // replace with original hook
        useExtractAudioPeaksMutation: () => null, // replace with original hook
        useExtractAudioPeaksQuery: (gsUtilPath: string, cacheKey: string[]) => null, // replace with original hook
        useGetVideoSignedUrlMutation: (gsUtilPath: string) => null, // replace with original hook
        useGetVideoSignedUrlQuery: (gsUtilPath: string, cacheKey: string[]) => null, // replace with original hook
      }}
    >
      <UserStoryScreen scenario="ready" isCitationLoadingNotStarted={true}>
        <UserStoryScreen.Content AtomicButton={AtomicButton}>
          <UserStoryScreen.Body>
            <UserStoryScreen.Story Component={UserStoryWithCitations} />
            <UserStoryScreen.Editor Component={RichTextEditor} />
            <UserStoryScreen.SubFeatures />
          </UserStoryScreen.Body>
          <UserStoryScreen.ScrollButton />
        </UserStoryScreen.Content>
      </UserStoryScreen>
    </RequirementAIWrapper>
  );
}
```

## Core Sub-Components

| Component                       | Purpose                                               |
| ------------------------------- | ----------------------------------------------------- |
| `UserStoryScreen.Sidebar`       | Navigation sidebar for story selection                |
| `UserStoryScreen.SidebarToggle` | Collapse/expand sidebar button                        |
| `UserStoryScreen.Content`       | Main content wrapper with header and controls         |
| `UserStoryScreen.Body`          | Container for Story/Editor (handles mutual exclusion) |
| `UserStoryScreen.Story`         | Displays selected story with citations (read mode)    |
| `UserStoryScreen.Editor`        | Rich text editor for editing stories (edit mode)      |
| `UserStoryScreen.SubFeatures`   | Accordion for nested sub-features                     |
| `UserStoryScreen.ScrollButton`  | Floating button to scroll to bottom                   |

## Root Props

```ts
interface UserStoryScreenProps {
  scenario: "streaming" | "ready"; // "streaming" for real-time, "ready" for static
  isCitationLoadingNotStarted: boolean; // Flag for citation loading state
  children: React.ReactNode; // Sub-components
  classNames?: UserStoryScreenClassNames; // Optional custom CSS classes
}
```

## Key Features

- **Automatic State Management**: All components pull from `RequirementAIWrapper` context
- **Mutual Exclusion**: Story and Editor views are automatically exclusive
- **Streaming Support**: Real-time updates with skeleton loaders
- **Citation Integration**: AI reasoning and citations highlighted in stories
- **Markdown Support**: Stories support markdown formatting with custom tags

## Usage Notes

1. **Always wrap with `RequirementAIWrapper`** - Provides context for all sub-components
2. **Use `Body` wrapper** - Ensures proper Story/Editor mutual exclusion
3. **Pass custom components** - Story and Editor expect React components as props
4. **Set scenario correctly** - "streaming" for real-time data, "ready" for static content

---

# UserStoryApprovalScreen Component

A dedicated approval workflow component for reviewing and approving AI-generated user stories. It must be wrapped within `RequirementAIWrapper` to access shared state.

## Overview

UserStoryApprovalScreen provides a structured interface for:

- Reviewing generated user stories segment-by-segment
- Editing individual content segments
- Approving content progressively
- Managing sub-features with delete capabilities
- Bulk approval with "Approve All" functionality

## Quick Demo

```tsx
import React, { useState } from "react";
import RequirementAIWrapper from "@e-llm-studio/requirement-ai";
import UserStoryApprovalScreen from "@e-llm-studio/requirement-ai";
import UserStoryWithCitations from "@e-llm-studio/requirement-ai";
import RichTextEditor from "@e-llm-studio/requirement-ai";
import AtomicButton from "@e-llm-studio/requirement-ai";

function MyApprovalFlow() {
  const [userStory, setUserStory] = useState<IUserStory[]>([]);
  const [aiReasoning, setAiReasoning] = useState<TAIReasoning[]>([]);
  const [citations, setCitations] = useState<UserStoryCitations[]>([]);

  return (
    <RequirementAIWrapper
      userStory={userStory}
      setUserStory={setUserStory}
      aiReasoning={aiReasoning}
      setAiReasoning={setAiReasoning}
      citations={citations}
      setCitations={setCitations}
      apiCachingConfig={{
        queryClient: null, // replace with original tanstack-query client
        useGetSignedUrlQuery: (gsUtilPath: string, cacheKey: string[]) => null, // replace with original hook
        useGetSignedUrlMutation: (gsUtilPath: string) => null, // replace with original hook
        useExtractAudioPeaksMutation: () => null, // replace with original hook
        useExtractAudioPeaksQuery: (gsUtilPath: string, cacheKey: string[]) => null, // replace with original hook
        useGetVideoSignedUrlMutation: (gsUtilPath: string) => null, // replace with original hook
        useGetVideoSignedUrlQuery: (gsUtilPath: string, cacheKey: string[]) => null, // replace with original hook
      }}
    >
      <UserStoryApprovalScreen
        artifactTitleIds={["STORY-001"]}
        config={{
          showApproveAllButton: true,
          allowEditing: true,
          allowDeletion: true,
          containerHeight: "calc(100vh - 100px)",
        }}
        callbacks={{
          onShowNotification: (type, title, message) => {
            console.log(`[${type}] ${title}: ${message}`);
          },
          onInitialize: () => {
            console.log("Approval screen initialized");
          },
          onSaveState: async (stories) => {
            await saveToRedis(stories);
          },
          onAllApproved: async (stories) => {
            await submitApprovedStories(stories);
          },
        }}
      >
        <UserStoryApprovalScreen.Content ButtonComponent={AtomicButton}>
          <UserStoryApprovalScreen.Body
            EditorComponent={RichTextEditor}
            ViewerComponent={UserStoryWithCitations}
          />
        </UserStoryApprovalScreen.Content>
      </UserStoryApprovalScreen>
    </RequirementAIWrapper>
  );
}
```

## Component Structure

| Component                         | Purpose                                                   |
| --------------------------------- | --------------------------------------------------------- |
| `UserStoryApprovalScreen` (Root)  | Container with approval state management                  |
| `UserStoryApprovalScreen.Content` | Header with title, metadata, and "Approve All" button     |
| `UserStoryApprovalScreen.Body`    | Renders content segments with edit/approve/delete actions |

---

## Root Component Props

```ts
interface UserStoryApprovalScreenProps {
  artifactTitleIds: string[]; // Display IDs for each story
  config?: UserStoryApprovalScreenConfig; // Feature toggles
  callbacks?: UserStoryApprovalScreenCallbacks; // Platform-specific handlers
  labels?: Partial<UserStoryApprovalScreenLabels>; // Custom text labels
  isDisabled?: boolean; // Disable all interactions
  children: React.ReactNode; // Sub-components
  classNames?: UserStoryApprovalScreenClassNames; // Custom CSS classes
}
```

## Configuration Options

```ts
interface UserStoryApprovalScreenConfig {
  showApproveAllButton?: boolean; // Show/hide "Approve All" button (default: true)
  allowEditing?: boolean; // Enable editing segments (default: true)
  allowDeletion?: boolean; // Enable deleting sub-features (default: true)
  showSidebar?: boolean; // Show sidebar for multi-story navigation (default: false)
  containerHeight?: string; // CSS height value (default: "calc(100vh - 100px)")
  useFullWidth?: boolean; // Use 100% width without margins (default: false)
}
```

## Callback Handlers

```ts
interface UserStoryApprovalScreenCallbacks {
  /** Show toast/notification - required for user feedback */
  onShowNotification?: (
    type: "success" | "error" | "warn" | "info",
    title: string,
    message: string,
    duration?: number,
  ) => void;

  /** Initialize platform (e.g., adjust chat width in web app) */
  onInitialize?: () => void;

  /** Persist approval state to Redis/storage */
  onSaveState?: (stories: IUserStoryForApproval[]) => Promise<void>;

  /** Triggered when all stories are approved */
  onAllApproved?: (stories: IUserStoryForApproval[]) => Promise<void>;

  /** Custom scroll behavior for focusing segments */
  onScrollToElement?: (element: HTMLElement) => void;
}
```

## Accessing Context

Use `useRequirementAI()` hook within UserStoryScreen components:

```tsx
import { useRequirementAI } from "@e-llm-studio/requirement-ai";

function MyCustomComponent() {
  const { userStory, setUserStory, aiReasoning } = useRequirementAI();
  return <div>{userStory?.[0]?.title}</div>;
}

```

---

## 🧩 Diff Edit Utilities – Usage Guide

### 1. `handleDiffEditApplied`

Applies AI diff response to user stories, reasoning, and chat history.

```ts
handleDiffEditApplied(
  aiResponseString,
  selectedUserStoryType,
  username,
  setUserStory,
  setAiReasoning,
  setAiReasoningDiffView,
  setChatHistory,
  setUserStoryDiffView
);
```

**Use when:**

* You receive AI diff-edit response
* You want to update UI with new stories, reasoning, and chat

---

### 2. `handleDiscardAll`

Rejects all diff changes and restores original state.

```ts
handleDiscardAll(
  userStory,
  setUserStory,
  setAiReasoning,
  setUserStoryDiffView
);
```

**Use when:**

* User clicks **“Discard All Changes”**
* You want to revert to original content

---

### 3. `handleAcceptAllChanges`

Accepts all diff changes and updates user stories.

```ts
handleAcceptAllChanges(
  userStory,
  setUserStory,
  setIsAutoSaveEnabled
);
```
## License

This library is released under the MIT License. See the LICENSE file for more details.

The license information is also available in the `citation/package.json` file under the 'license' field.
