• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

peteretelej / md-server / 17193676866

24 Aug 2025 08:56PM UTC coverage: 91.351% (-8.0%) from 99.312%
17193676866

Pull #7

github

web-flow
Merge 8ac64f79f into e594bd672
Pull Request #7: WIP: Python SDK

99 of 109 branches covered (90.83%)

Branch coverage included in aggregate %.

384 of 436 new or added lines in 13 files covered. (88.07%)

8 existing lines in 2 files now uncovered.

746 of 816 relevant lines covered (91.42%)

0.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.14
/src/md_server/models.py
1
from pydantic import BaseModel, Field
1✔
2
from typing import Optional, List, Dict, Any
1✔
3
import uuid
1✔
4

5

6
class ConversionOptions(BaseModel):
1✔
7
    js_rendering: Optional[bool] = Field(
1✔
8
        default=None, description="Use headless browser for JavaScript sites"
9
    )
10
    timeout: Optional[int] = Field(default=None, description="Timeout in seconds")
1✔
11
    extract_images: Optional[bool] = Field(
1✔
12
        default=False, description="Extract and link images"
13
    )
14
    preserve_formatting: Optional[bool] = Field(
1✔
15
        default=True, description="Preserve complex formatting"
16
    )
17
    ocr_enabled: Optional[bool] = Field(
1✔
18
        default=False, description="Enable OCR for images/PDFs"
19
    )
20
    max_length: Optional[int] = Field(
1✔
21
        default=None, description="Truncate output for previews"
22
    )
23
    clean_markdown: Optional[bool] = Field(
1✔
24
        default=False, description="Normalize/clean markdown output"
25
    )
26

27

28
class ConvertRequest(BaseModel):
1✔
29
    url: Optional[str] = Field(default=None, description="URL to fetch and convert")
1✔
30
    content: Optional[str] = Field(
1✔
31
        default=None, description="Base64 encoded file content"
32
    )
33
    text: Optional[str] = Field(default=None, description="Raw text content")
1✔
34
    mime_type: Optional[str] = Field(
1✔
35
        default=None, description="MIME type for text content"
36
    )
37
    filename: Optional[str] = Field(
1✔
38
        default=None, description="Original filename for format detection"
39
    )
40
    source_format: Optional[str] = Field(
1✔
41
        default=None, description="Explicit source format override"
42
    )
43
    options: Optional[ConversionOptions] = Field(
1✔
44
        default=None, description="Processing options"
45
    )
46

47
    def model_post_init(self, __context: Any) -> None:
1✔
UNCOV
48
        input_count = sum(
×
49
            1 for field in [self.url, self.content, self.text] if field is not None
50
        )
UNCOV
51
        if input_count == 0:
×
UNCOV
52
            raise ValueError("One of url, content, or text must be provided")
×
UNCOV
53
        if input_count > 1:
×
UNCOV
54
            raise ValueError("Only one of url, content, or text can be provided")
×
55

56

57
class ConversionMetadata(BaseModel):
1✔
58
    source_type: str = Field(description="Type of source content (pdf, html, etc.)")
1✔
59
    source_size: int = Field(description="Size of source content in bytes")
1✔
60
    markdown_size: int = Field(description="Size of converted markdown in bytes")
1✔
61
    conversion_time_ms: int = Field(
1✔
62
        description="Time taken for conversion in milliseconds"
63
    )
64
    detected_format: str = Field(description="Detected format/MIME type")
1✔
65
    warnings: List[str] = Field(default_factory=list, description="Conversion warnings")
1✔
66

67

68
class ConversionResult(BaseModel):
1✔
69
    """Result of a document conversion operation from core converter."""
70

71
    success: bool = Field(description="Whether conversion was successful")
1✔
72
    markdown: str = Field(description="Converted markdown content")
1✔
73
    metadata: ConversionMetadata = Field(description="Conversion metadata")
1✔
74
    request_id: str = Field(
1✔
75
        default_factory=lambda: f"req_{uuid.uuid4()}",
76
        description="Unique request identifier",
77
    )
78

79

80
class ConvertResponse(BaseModel):
1✔
81
    success: bool = Field(description="Whether conversion was successful")
1✔
82
    markdown: str = Field(description="Converted markdown content")
1✔
83
    metadata: ConversionMetadata = Field(description="Conversion metadata")
1✔
84
    request_id: str = Field(description="Unique request identifier")
1✔
85

86
    @classmethod
1✔
87
    def create_success(
1✔
88
        cls,
89
        markdown: str,
90
        source_type: str,
91
        source_size: int,
92
        conversion_time_ms: int,
93
        detected_format: str,
94
        warnings: Optional[List[str]] = None,
95
    ) -> "ConvertResponse":
96
        return cls(
1✔
97
            success=True,
98
            markdown=markdown,
99
            metadata=ConversionMetadata(
100
                source_type=source_type,
101
                source_size=source_size,
102
                markdown_size=len(markdown.encode("utf-8")),
103
                conversion_time_ms=conversion_time_ms,
104
                detected_format=detected_format,
105
                warnings=warnings or [],
106
            ),
107
            request_id=f"req_{uuid.uuid4()}",
108
        )
109

110

111
class ErrorDetails(BaseModel):
1✔
112
    detected_format: Optional[str] = None
1✔
113
    supported_formats: Optional[List[str]] = None
1✔
114
    magic_bytes: Optional[str] = None
1✔
115

116

117
class ConvertError(BaseModel):
1✔
118
    code: str = Field(description="Error code")
1✔
119
    message: str = Field(description="Human readable error message")
1✔
120
    details: Optional[Dict[str, Any]] = Field(
1✔
121
        default=None, description="Additional error details"
122
    )
123
    suggestions: Optional[List[str]] = Field(
1✔
124
        default=None, description="Suggested actions"
125
    )
126

127

128
class ErrorResponse(BaseModel):
1✔
129
    success: bool = Field(default=False, description="Always false for errors")
1✔
130
    error: ConvertError = Field(description="Error information")
1✔
131
    request_id: str = Field(description="Unique request identifier")
1✔
132

133
    @classmethod
1✔
134
    def create_error(
1✔
135
        cls,
136
        code: str,
137
        message: str,
138
        details: Optional[Dict[str, Any]] = None,
139
        suggestions: Optional[List[str]] = None,
140
    ) -> "ErrorResponse":
141
        return cls(
1✔
142
            error=ConvertError(
143
                code=code, message=message, details=details, suggestions=suggestions
144
            ),
145
            request_id=f"req_{uuid.uuid4()}",
146
        )
147

148

149
# Models for /formats endpoint
150
class FormatCapabilities(BaseModel):
1✔
151
    mime_types: List[str] = Field(description="Supported MIME types")
1✔
152
    extensions: List[str] = Field(description="Supported file extensions")
1✔
153
    features: List[str] = Field(description="Available features for this format")
1✔
154
    max_size_mb: int = Field(description="Maximum file size in MB")
1✔
155

156

157
class SystemCapabilities(BaseModel):
1✔
158
    browser_available: bool = Field(description="Whether browser support is available")
1✔
159

160

161
class FormatsResponse(BaseModel):
1✔
162
    formats: Dict[str, FormatCapabilities] = Field(
1✔
163
        description="Supported formats and their capabilities"
164
    )
165
    supported_formats: List[str] = Field(description="List of supported format names")
1✔
166
    capabilities: SystemCapabilities = Field(
1✔
167
        description="System capability information"
168
    )
169

170

171
# Models for /health endpoint
172
class HealthResponse(BaseModel):
1✔
173
    status: str = Field(description="Health status")
1✔
174
    version: str = Field(description="Application version")
1✔
175
    uptime_seconds: int = Field(description="Server uptime in seconds")
1✔
176
    conversions_last_hour: Optional[int] = Field(
1✔
177
        default=0, description="Number of conversions in the last hour"
178
    )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc