|
| 1 | +"""Models for rlsapi v1 REST API requests.""" |
| 2 | + |
| 3 | +from pydantic import Field, field_validator |
| 4 | + |
| 5 | +from models.config import ConfigurationBase |
| 6 | + |
| 7 | + |
| 8 | +class RlsapiV1Attachment(ConfigurationBase): |
| 9 | + """Attachment data from rlsapi v1 context. |
| 10 | +
|
| 11 | + Attributes: |
| 12 | + contents: The textual contents of the file read on the client machine. |
| 13 | + mimetype: The MIME type of the file. |
| 14 | + """ |
| 15 | + |
| 16 | + contents: str = Field( |
| 17 | + default="", |
| 18 | + description="File contents read on client", |
| 19 | + examples=["# Configuration file\nkey=value"], |
| 20 | + ) |
| 21 | + mimetype: str = Field( |
| 22 | + default="", |
| 23 | + description="MIME type of the file", |
| 24 | + examples=["text/plain", "application/json"], |
| 25 | + ) |
| 26 | + |
| 27 | + |
| 28 | +class RlsapiV1Terminal(ConfigurationBase): |
| 29 | + """Terminal output from rlsapi v1 context. |
| 30 | +
|
| 31 | + Attributes: |
| 32 | + output: The textual contents of the terminal read on the client machine. |
| 33 | + """ |
| 34 | + |
| 35 | + output: str = Field( |
| 36 | + default="", |
| 37 | + description="Terminal output from client", |
| 38 | + examples=["bash: command not found", "Permission denied"], |
| 39 | + ) |
| 40 | + |
| 41 | + |
| 42 | +class RlsapiV1SystemInfo(ConfigurationBase): |
| 43 | + """System information from rlsapi v1 context. |
| 44 | +
|
| 45 | + Attributes: |
| 46 | + os: The operating system of the client machine. |
| 47 | + version: The version of the operating system. |
| 48 | + arch: The architecture of the client machine. |
| 49 | + system_id: The id of the client machine. |
| 50 | + """ |
| 51 | + |
| 52 | + os: str = Field(default="", description="Operating system name", examples=["RHEL"]) |
| 53 | + version: str = Field( |
| 54 | + default="", description="Operating system version", examples=["9.3", "8.10"] |
| 55 | + ) |
| 56 | + arch: str = Field( |
| 57 | + default="", description="System architecture", examples=["x86_64", "aarch64"] |
| 58 | + ) |
| 59 | + system_id: str = Field( |
| 60 | + default="", |
| 61 | + alias="id", |
| 62 | + description="Client machine ID", |
| 63 | + examples=["01JDKR8N7QW9ZMXVGK3PB5TQWZ"], |
| 64 | + ) |
| 65 | + |
| 66 | + model_config = {"populate_by_name": True} |
| 67 | + |
| 68 | + |
| 69 | +class RlsapiV1CLA(ConfigurationBase): |
| 70 | + """Command Line Assistant information from rlsapi v1 context. |
| 71 | +
|
| 72 | + Attributes: |
| 73 | + nevra: The NEVRA (Name-Epoch-Version-Release-Architecture) of the CLA. |
| 74 | + version: The version of the command line assistant. |
| 75 | + """ |
| 76 | + |
| 77 | + nevra: str = Field( |
| 78 | + default="", |
| 79 | + description="CLA NEVRA identifier", |
| 80 | + examples=["command-line-assistant-0:0.2.0-1.el9.noarch"], |
| 81 | + ) |
| 82 | + version: str = Field( |
| 83 | + default="", |
| 84 | + description="Command line assistant version", |
| 85 | + examples=["0.2.0"], |
| 86 | + ) |
| 87 | + |
| 88 | + |
| 89 | +class RlsapiV1Context(ConfigurationBase): |
| 90 | + """Context data for rlsapi v1 /infer request. |
| 91 | +
|
| 92 | + Attributes: |
| 93 | + stdin: Redirect input read by command-line-assistant. |
| 94 | + attachments: Attachment object received by the client. |
| 95 | + terminal: Terminal object received by the client. |
| 96 | + systeminfo: System information object received by the client. |
| 97 | + cla: Command Line Assistant information. |
| 98 | + """ |
| 99 | + |
| 100 | + stdin: str = Field( |
| 101 | + default="", |
| 102 | + description="Redirect input from stdin", |
| 103 | + examples=["piped input from previous command"], |
| 104 | + ) |
| 105 | + attachments: RlsapiV1Attachment = Field( |
| 106 | + default_factory=RlsapiV1Attachment, |
| 107 | + description="File attachment data", |
| 108 | + ) |
| 109 | + terminal: RlsapiV1Terminal = Field( |
| 110 | + default_factory=RlsapiV1Terminal, |
| 111 | + description="Terminal output context", |
| 112 | + ) |
| 113 | + systeminfo: RlsapiV1SystemInfo = Field( |
| 114 | + default_factory=RlsapiV1SystemInfo, |
| 115 | + description="Client system information", |
| 116 | + ) |
| 117 | + cla: RlsapiV1CLA = Field( |
| 118 | + default_factory=RlsapiV1CLA, |
| 119 | + description="Command line assistant metadata", |
| 120 | + ) |
| 121 | + |
| 122 | + |
| 123 | +class RlsapiV1InferRequest(ConfigurationBase): |
| 124 | + """RHEL Lightspeed rlsapi v1 /infer request. |
| 125 | +
|
| 126 | + Attributes: |
| 127 | + question: User question string. |
| 128 | + context: Context with system info, terminal output, etc. (defaults provided). |
| 129 | + skip_rag: Whether to skip RAG retrieval (default False). |
| 130 | +
|
| 131 | + Example: |
| 132 | + ```python |
| 133 | + request = RlsapiV1InferRequest( |
| 134 | + question="How do I list files?", |
| 135 | + context=RlsapiV1Context( |
| 136 | + systeminfo=RlsapiV1SystemInfo(os="RHEL", version="9.3"), |
| 137 | + terminal=RlsapiV1Terminal(output="bash: command not found"), |
| 138 | + ), |
| 139 | + ) |
| 140 | + ``` |
| 141 | + """ |
| 142 | + |
| 143 | + question: str = Field( |
| 144 | + ..., |
| 145 | + min_length=1, |
| 146 | + description="User question", |
| 147 | + examples=["How do I list files?", "How do I configure SELinux?"], |
| 148 | + ) |
| 149 | + context: RlsapiV1Context = Field( |
| 150 | + default_factory=RlsapiV1Context, |
| 151 | + description="Optional context (system info, terminal output, stdin, attachments)", |
| 152 | + ) |
| 153 | + skip_rag: bool = Field( |
| 154 | + default=False, |
| 155 | + description="Whether to skip RAG retrieval", |
| 156 | + examples=[False, True], |
| 157 | + ) |
| 158 | + |
| 159 | + @field_validator("question") |
| 160 | + @classmethod |
| 161 | + def validate_question(cls, value: str) -> str: |
| 162 | + """Validate question is not empty or whitespace-only. |
| 163 | +
|
| 164 | + Args: |
| 165 | + value: The question string to validate. |
| 166 | +
|
| 167 | + Returns: |
| 168 | + The stripped question string. |
| 169 | +
|
| 170 | + Raises: |
| 171 | + ValueError: If the question is empty or whitespace-only. |
| 172 | + """ |
| 173 | + stripped = value.strip() |
| 174 | + if not stripped: |
| 175 | + raise ValueError("Question cannot be empty or whitespace-only") |
| 176 | + return stripped |
0 commit comments