Skip to content

Commit f8edcc2

Browse files
authored
Support date/time types (#218)
* Add date/time types support in Rust core. * Add date/time type support in Python SDK. * JSON schema for OpenAI. * Improved fault tolerant in parsing datetime.
1 parent 7c590c2 commit f8edcc2

File tree

10 files changed

+247
-15
lines changed

10 files changed

+247
-15
lines changed

Cargo.toml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ name = "cocoindex_engine"
1111
crate-type = ["cdylib"]
1212

1313
[dependencies]
14-
pyo3 = { version = "0.23.5" }
14+
pyo3 = { version = "0.23.5", features = ["chrono"] }
1515
anyhow = { version = "1.0.97", features = ["std"] }
1616
async-trait = "0.1.88"
1717
axum = "0.7.9"
@@ -25,7 +25,12 @@ log = "0.4.26"
2525
regex = "1.11.1"
2626
serde = { version = "1.0.219", features = ["derive"] }
2727
serde_json = "1.0.140"
28-
sqlx = { version = "0.8.3", features = ["chrono", "postgres", "runtime-tokio", "uuid"] }
28+
sqlx = { version = "0.8.3", features = [
29+
"chrono",
30+
"postgres",
31+
"runtime-tokio",
32+
"uuid",
33+
] }
2934
tokio = { version = "1.44.1", features = [
3035
"macros",
3136
"rt-multi-thread",

python/cocoindex/typing.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import typing
22
import collections
33
import dataclasses
4+
import datetime
45
import types
56
import inspect
67
import uuid
@@ -26,6 +27,8 @@ def __init__(self, key: str, value: Any):
2627
Float64 = Annotated[float, TypeKind('Float64')]
2728
Range = Annotated[tuple[int, int], TypeKind('Range')]
2829
Json = Annotated[Any, TypeKind('Json')]
30+
LocalDateTime = Annotated[datetime.datetime, TypeKind('LocalDateTime')]
31+
OffsetDateTime = Annotated[datetime.datetime, TypeKind('OffsetDateTime')]
2932

3033
COLLECTION_TYPES = ('Table', 'List')
3134

@@ -133,6 +136,12 @@ def analyze_type_info(t) -> AnalyzedTypeInfo:
133136
kind = 'Float64'
134137
elif t is uuid.UUID:
135138
kind = 'Uuid'
139+
elif t is datetime.date:
140+
kind = 'Date'
141+
elif t is datetime.time:
142+
kind = 'Time'
143+
elif t is datetime.datetime:
144+
kind = 'OffsetDateTime'
136145
else:
137146
raise ValueError(f"type unsupported yet: {t}")
138147

src/base/json_schema.rs

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ pub struct ToJsonSchemaOptions {
88
/// Use union type (with `null`) for optional fields instead.
99
/// Models like OpenAI will reject the schema if a field is not required.
1010
pub fields_always_required: bool,
11+
12+
/// If true, the JSON schema supports the `format` keyword.
13+
pub supports_format: bool,
1114
}
1215

1316
pub trait ToJsonSchema {
@@ -49,15 +52,51 @@ impl ToJsonSchema for schema::BasicValueType {
4952
max_items: Some(2),
5053
..Default::default()
5154
}));
52-
schema
53-
.metadata
54-
.get_or_insert_with(Default::default)
55-
.description =
55+
schema.metadata.get_or_insert_default().description =
5656
Some("A range, start pos (inclusive), end pos (exclusive).".to_string());
5757
}
5858
schema::BasicValueType::Uuid => {
5959
schema.instance_type = Some(SingleOrVec::Single(Box::new(InstanceType::String)));
60-
schema.format = Some("uuid".to_string());
60+
if options.supports_format {
61+
schema.format = Some("uuid".to_string());
62+
} else {
63+
schema.metadata.get_or_insert_default().description =
64+
Some("A UUID, e.g. 123e4567-e89b-12d3-a456-426614174000".to_string());
65+
}
66+
}
67+
schema::BasicValueType::Date => {
68+
schema.instance_type = Some(SingleOrVec::Single(Box::new(InstanceType::String)));
69+
if options.supports_format {
70+
schema.format = Some("date".to_string());
71+
} else {
72+
schema.metadata.get_or_insert_default().description =
73+
Some("A date, e.g. 2025-03-27".to_string());
74+
}
75+
}
76+
schema::BasicValueType::Time => {
77+
schema.instance_type = Some(SingleOrVec::Single(Box::new(InstanceType::String)));
78+
if options.supports_format {
79+
schema.format = Some("time".to_string());
80+
} else {
81+
schema.metadata.get_or_insert_default().description =
82+
Some("A time, e.g. 13:32:12".to_string());
83+
}
84+
}
85+
schema::BasicValueType::LocalDateTime => {
86+
schema.instance_type = Some(SingleOrVec::Single(Box::new(InstanceType::String)));
87+
if options.supports_format {
88+
schema.format = Some("date-time".to_string());
89+
}
90+
schema.metadata.get_or_insert_default().description =
91+
Some("Date time without timezone offset, e.g. 2025-03-27T13:32:12".to_string());
92+
}
93+
schema::BasicValueType::OffsetDateTime => {
94+
schema.instance_type = Some(SingleOrVec::Single(Box::new(InstanceType::String)));
95+
if options.supports_format {
96+
schema.format = Some("date-time".to_string());
97+
}
98+
schema.metadata.get_or_insert_default().description =
99+
Some("Date time with timezone offset in RFC3339, e.g. 2025-03-27T13:32:12Z, 2025-03-27T07:32:12.313-06:00".to_string());
61100
}
62101
schema::BasicValueType::Json => {
63102
// Can be any value. No type constraint.

src/base/schema.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,18 @@ pub enum BasicValueType {
3838
/// A UUID.
3939
Uuid,
4040

41+
/// Date (without time within the current day).
42+
Date,
43+
44+
/// Time of the day.
45+
Time,
46+
47+
/// Local date and time, without timezone.
48+
LocalDateTime,
49+
50+
/// Date and time with timezone.
51+
OffsetDateTime,
52+
4153
/// A JSON value.
4254
Json,
4355

@@ -56,6 +68,10 @@ impl std::fmt::Display for BasicValueType {
5668
BasicValueType::Float64 => write!(f, "float64"),
5769
BasicValueType::Range => write!(f, "range"),
5870
BasicValueType::Uuid => write!(f, "uuid"),
71+
BasicValueType::Date => write!(f, "date"),
72+
BasicValueType::Time => write!(f, "time"),
73+
BasicValueType::LocalDateTime => write!(f, "local_datetime"),
74+
BasicValueType::OffsetDateTime => write!(f, "offset_datetime"),
5975
BasicValueType::Json => write!(f, "json"),
6076
BasicValueType::Vector(s) => write!(
6177
f,

0 commit comments

Comments
 (0)